1 /*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javax.swing;
27
28 import java.util.*;
29
30 import java.applet.Applet;
31 import java.awt.*;
32 import java.awt.event.*;
33 import java.awt.print.*;
34
35 import java.beans.*;
36
37 import java.io.Serializable;
38 import java.io.ObjectOutputStream;
39 import java.io.ObjectInputStream;
40 import java.io.IOException;
41
42 import javax.accessibility.*;
43
44 import javax.swing.event.*;
45 import javax.swing.plaf.*;
46 import javax.swing.table.*;
47 import javax.swing.border.*;
48
49 import java.text.NumberFormat;
50 import java.text.DateFormat;
51 import java.text.MessageFormat;
52
53 import javax.print.attribute.*;
54 import javax.print.PrintService;
55
56 import sun.swing.SwingUtilities2;
57 import sun.swing.SwingUtilities2.Section;
58 import static sun.swing.SwingUtilities2.Section.*;
59 import sun.swing.PrintingStatus;
60 import sun.swing.SwingLazyValue;
61
62 /**
63 * The <code>JTable</code> is used to display and edit regular two-dimensional tables
64 * of cells.
65 * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/table.html">How to Use Tables</a>
66 * in <em>The Java Tutorial</em>
67 * for task-oriented documentation and examples of using <code>JTable</code>.
68 *
69 * <p>
70 * The <code>JTable</code> has many
71 * facilities that make it possible to customize its rendering and editing
72 * but provides defaults for these features so that simple tables can be
73 * set up easily. For example, to set up a table with 10 rows and 10
74 * columns of numbers:
75 * <p>
76 * <pre>
77 * TableModel dataModel = new AbstractTableModel() {
78 * public int getColumnCount() { return 10; }
79 * public int getRowCount() { return 10;}
80 * public Object getValueAt(int row, int col) { return new Integer(row*col); }
81 * };
82 * JTable table = new JTable(dataModel);
83 * JScrollPane scrollpane = new JScrollPane(table);
84 * </pre>
85 * <p>
86 * {@code JTable}s are typically placed inside of a {@code JScrollPane}. By
87 * default, a {@code JTable} will adjust its width such that
88 * a horizontal scrollbar is unnecessary. To allow for a horizontal scrollbar,
89 * invoke {@link #setAutoResizeMode} with {@code AUTO_RESIZE_OFF}.
90 * Note that if you wish to use a <code>JTable</code> in a standalone
91 * view (outside of a <code>JScrollPane</code>) and want the header
92 * displayed, you can get it using {@link #getTableHeader} and
93 * display it separately.
94 * <p>
95 * To enable sorting and filtering of rows, use a
96 * {@code RowSorter}.
97 * You can set up a row sorter in either of two ways:
98 * <ul>
99 * <li>Directly set the {@code RowSorter}. For example:
100 * {@code table.setRowSorter(new TableRowSorter(model))}.
101 * <li>Set the {@code autoCreateRowSorter}
102 * property to {@code true}, so that the {@code JTable}
103 * creates a {@code RowSorter} for
104 * you. For example: {@code setAutoCreateRowSorter(true)}.
105 * </ul>
106 * <p>
107 * When designing applications that use the <code>JTable</code> it is worth paying
108 * close attention to the data structures that will represent the table's data.
109 * The <code>DefaultTableModel</code> is a model implementation that
110 * uses a <code>Vector</code> of <code>Vector</code>s of <code>Object</code>s to
111 * store the cell values. As well as copying the data from an
112 * application into the <code>DefaultTableModel</code>,
113 * it is also possible to wrap the data in the methods of the
114 * <code>TableModel</code> interface so that the data can be passed to the
115 * <code>JTable</code> directly, as in the example above. This often results
116 * in more efficient applications because the model is free to choose the
117 * internal representation that best suits the data.
118 * A good rule of thumb for deciding whether to use the <code>AbstractTableModel</code>
119 * or the <code>DefaultTableModel</code> is to use the <code>AbstractTableModel</code>
120 * as the base class for creating subclasses and the <code>DefaultTableModel</code>
121 * when subclassing is not required.
122 * <p>
123 * The "TableExample" directory in the demo area of the source distribution
124 * gives a number of complete examples of <code>JTable</code> usage,
125 * covering how the <code>JTable</code> can be used to provide an
126 * editable view of data taken from a database and how to modify
127 * the columns in the display to use specialized renderers and editors.
128 * <p>
129 * The <code>JTable</code> uses integers exclusively to refer to both the rows and the columns
130 * of the model that it displays. The <code>JTable</code> simply takes a tabular range of cells
131 * and uses <code>getValueAt(int, int)</code> to retrieve the
132 * values from the model during painting. It is important to remember that
133 * the column and row indexes returned by various <code>JTable</code> methods
134 * are in terms of the <code>JTable</code> (the view) and are not
135 * necessarily the same indexes used by the model.
136 * <p>
137 * By default, columns may be rearranged in the <code>JTable</code> so that the
138 * view's columns appear in a different order to the columns in the model.
139 * This does not affect the implementation of the model at all: when the
140 * columns are reordered, the <code>JTable</code> maintains the new order of the columns
141 * internally and converts its column indices before querying the model.
142 * <p>
143 * So, when writing a <code>TableModel</code>, it is not necessary to listen for column
144 * reordering events as the model will be queried in its own coordinate
145 * system regardless of what is happening in the view.
146 * In the examples area there is a demonstration of a sorting algorithm making
147 * use of exactly this technique to interpose yet another coordinate system
148 * where the order of the rows is changed, rather than the order of the columns.
149 * <p>
150 * Similarly when using the sorting and filtering functionality
151 * provided by <code>RowSorter</code> the underlying
152 * <code>TableModel</code> does not need to know how to do sorting,
153 * rather <code>RowSorter</code> will handle it. Coordinate
154 * conversions will be necessary when using the row based methods of
155 * <code>JTable</code> with the underlying <code>TableModel</code>.
156 * All of <code>JTable</code>s row based methods are in terms of the
157 * <code>RowSorter</code>, which is not necessarily the same as that
158 * of the underlying <code>TableModel</code>. For example, the
159 * selection is always in terms of <code>JTable</code> so that when
160 * using <code>RowSorter</code> you will need to convert using
161 * <code>convertRowIndexToView</code> or
162 * <code>convertRowIndexToModel</code>. The following shows how to
163 * convert coordinates from <code>JTable</code> to that of the
164 * underlying model:
165 * <pre>
166 * int[] selection = table.getSelectedRows();
167 * for (int i = 0; i < selection.length; i++) {
168 * selection[i] = table.convertRowIndexToModel(selection[i]);
169 * }
170 * // selection is now in terms of the underlying TableModel
171 * </pre>
172 * <p>
173 * By default if sorting is enabled <code>JTable</code> will persist the
174 * selection and variable row heights in terms of the model on
175 * sorting. For example if row 0, in terms of the underlying model,
176 * is currently selected, after the sort row 0, in terms of the
177 * underlying model will be selected. Visually the selection may
178 * change, but in terms of the underlying model it will remain the
179 * same. The one exception to that is if the model index is no longer
180 * visible or was removed. For example, if row 0 in terms of model
181 * was filtered out the selection will be empty after the sort.
182 * <p>
183 * J2SE 5 adds methods to <code>JTable</code> to provide convenient access to some
184 * common printing needs. Simple new {@link #print()} methods allow for quick
185 * and easy addition of printing support to your application. In addition, a new
186 * {@link #getPrintable} method is available for more advanced printing needs.
187 * <p>
188 * As for all <code>JComponent</code> classes, you can use
189 * {@link InputMap} and {@link ActionMap} to associate an
190 * {@link Action} object with a {@link KeyStroke} and execute the
191 * action under specified conditions.
192 * <p>
193 * <strong>Warning:</strong> Swing is not thread safe. For more
194 * information see <a
195 * href="package-summary.html#threading">Swing's Threading
196 * Policy</a>.
197 * <p>
198 * <strong>Warning:</strong>
199 * Serialized objects of this class will not be compatible with
200 * future Swing releases. The current serialization support is
201 * appropriate for short term storage or RMI between applications running
202 * the same version of Swing. As of 1.4, support for long term storage
203 * of all JavaBeans<sup><font size="-2">TM</font></sup>
204 * has been added to the <code>java.beans</code> package.
205 * Please see {@link java.beans.XMLEncoder}.
206 *
207 *
208 * @beaninfo
209 * attribute: isContainer false
210 * description: A component which displays data in a two dimensional grid.
211 *
212 * @author Philip Milne
213 * @author Shannon Hickey (printing support)
214 * @see javax.swing.table.DefaultTableModel
215 * @see javax.swing.table.TableRowSorter
216 */
217 /* The first versions of the JTable, contained in Swing-0.1 through
218 * Swing-0.4, were written by Alan Chung.
219 */
220 public class JTable extends JComponent implements TableModelListener, Scrollable,
221 TableColumnModelListener, ListSelectionListener, CellEditorListener,
222 Accessible, RowSorterListener
223 {
224 //
225 // Static Constants
226 //
227
228 /**
229 * @see #getUIClassID
230 * @see #readObject
231 */
232 private static final String uiClassID = "TableUI";
233
234 /** Do not adjust column widths automatically; use a horizontal scrollbar instead. */
235 public static final int AUTO_RESIZE_OFF = 0;
236
237 /** When a column is adjusted in the UI, adjust the next column the opposite way. */
238 public static final int AUTO_RESIZE_NEXT_COLUMN = 1;
239
240 /** During UI adjustment, change subsequent columns to preserve the total width;
241 * this is the default behavior. */
242 public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2;
243
244 /** During all resize operations, apply adjustments to the last column only. */
245 public static final int AUTO_RESIZE_LAST_COLUMN = 3;
246
247 /** During all resize operations, proportionately resize all columns. */
248 public static final int AUTO_RESIZE_ALL_COLUMNS = 4;
249
250
251 /**
252 * Printing modes, used in printing <code>JTable</code>s.
253 *
254 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat,
255 * boolean, PrintRequestAttributeSet, boolean)
256 * @see #getPrintable
257 * @since 1.5
258 */
259 public enum PrintMode {
260
261 /**
262 * Printing mode that prints the table at its current size,
263 * spreading both columns and rows across multiple pages if necessary.
264 */
265 NORMAL,
266
267 /**
268 * Printing mode that scales the output smaller, if necessary,
269 * to fit the table's entire width (and thereby all columns) on each page;
270 * Rows are spread across multiple pages as necessary.
271 */
272 FIT_WIDTH
273 }
274
275
276 //
277 // Instance Variables
278 //
279
280 /** The <code>TableModel</code> of the table. */
281 protected TableModel dataModel;
282
283 /** The <code>TableColumnModel</code> of the table. */
284 protected TableColumnModel columnModel;
285
286 /** The <code>ListSelectionModel</code> of the table, used to keep track of row selections. */
287 protected ListSelectionModel selectionModel;
288
289 /** The <code>TableHeader</code> working with the table. */
290 protected JTableHeader tableHeader;
291
292 /** The height in pixels of each row in the table. */
293 protected int rowHeight;
294
295 /** The height in pixels of the margin between the cells in each row. */
296 protected int rowMargin;
297
298 /** The color of the grid. */
299 protected Color gridColor;
300
301 /** The table draws horizontal lines between cells if <code>showHorizontalLines</code> is true. */
302 protected boolean showHorizontalLines;
303
304 /** The table draws vertical lines between cells if <code>showVerticalLines</code> is true. */
305 protected boolean showVerticalLines;
306
307 /**
308 * Determines if the table automatically resizes the
309 * width of the table's columns to take up the entire width of the
310 * table, and how it does the resizing.
311 */
312 protected int autoResizeMode;
313
314 /**
315 * The table will query the <code>TableModel</code> to build the default
316 * set of columns if this is true.
317 */
318 protected boolean autoCreateColumnsFromModel;
319
320 /** Used by the <code>Scrollable</code> interface to determine the initial visible area. */
321 protected Dimension preferredViewportSize;
322
323 /** True if row selection is allowed in this table. */
324 protected boolean rowSelectionAllowed;
325
326 /**
327 * Obsolete as of Java 2 platform v1.3. Please use the
328 * <code>rowSelectionAllowed</code> property and the
329 * <code>columnSelectionAllowed</code> property of the
330 * <code>columnModel</code> instead. Or use the
331 * method <code>getCellSelectionEnabled</code>.
332 */
333 /*
334 * If true, both a row selection and a column selection
335 * can be non-empty at the same time, the selected cells are the
336 * the cells whose row and column are both selected.
337 */
338 protected boolean cellSelectionEnabled;
339
340 /** If editing, the <code>Component</code> that is handling the editing. */
341 transient protected Component editorComp;
342
343 /**
344 * The active cell editor object, that overwrites the screen real estate
345 * occupied by the current cell and allows the user to change its contents.
346 * {@code null} if the table isn't currently editing.
347 */
348 transient protected TableCellEditor cellEditor;
349
350 /** Identifies the column of the cell being edited. */
351 transient protected int editingColumn;
352
353 /** Identifies the row of the cell being edited. */
354 transient protected int editingRow;
355
356 /**
357 * A table of objects that display the contents of a cell,
358 * indexed by class as declared in <code>getColumnClass</code>
359 * in the <code>TableModel</code> interface.
360 */
361 transient protected Hashtable defaultRenderersByColumnClass;
362
363 /**
364 * A table of objects that display and edit the contents of a cell,
365 * indexed by class as declared in <code>getColumnClass</code>
366 * in the <code>TableModel</code> interface.
367 */
368 transient protected Hashtable defaultEditorsByColumnClass;
369
370 /** The foreground color of selected cells. */
371 protected Color selectionForeground;
372
373 /** The background color of selected cells. */
374 protected Color selectionBackground;
375
376 //
377 // Private state
378 //
379
380 // WARNING: If you directly access this field you should also change the
381 // SortManager.modelRowSizes field as well.
382 private SizeSequence rowModel;
383 private boolean dragEnabled;
384 private boolean surrendersFocusOnKeystroke;
385 private PropertyChangeListener editorRemover = null;
386 /**
387 * The last value of getValueIsAdjusting from the column selection models
388 * columnSelectionChanged notification. Used to test if a repaint is
389 * needed.
390 */
391 private boolean columnSelectionAdjusting;
392 /**
393 * The last value of getValueIsAdjusting from the row selection models
394 * valueChanged notification. Used to test if a repaint is needed.
395 */
396 private boolean rowSelectionAdjusting;
397
398 /**
399 * To communicate errors between threads during printing.
400 */
401 private Throwable printError;
402
403 /**
404 * True when setRowHeight(int) has been invoked.
405 */
406 private boolean isRowHeightSet;
407
408 /**
409 * If true, on a sort the selection is reset.
410 */
411 private boolean updateSelectionOnSort;
412
413 /**
414 * Information used in sorting.
415 */
416 private transient SortManager sortManager;
417
418 /**
419 * If true, when sorterChanged is invoked it's value is ignored.
420 */
421 private boolean ignoreSortChange;
422
423 /**
424 * Whether or not sorterChanged has been invoked.
425 */
426 private boolean sorterChanged;
427
428 /**
429 * If true, any time the model changes a new RowSorter is set.
430 */
431 private boolean autoCreateRowSorter;
432
433 /**
434 * Whether or not the table always fills the viewport height.
435 * @see #setFillsViewportHeight
436 * @see #getScrollableTracksViewportHeight
437 */
438 private boolean fillsViewportHeight;
439
440 /**
441 * The drop mode for this component.
442 */
443 private DropMode dropMode = DropMode.USE_SELECTION;
444
445 /**
446 * The drop location.
447 */
448 private transient DropLocation dropLocation;
449
450 /**
451 * A subclass of <code>TransferHandler.DropLocation</code> representing
452 * a drop location for a <code>JTable</code>.
453 *
454 * @see #getDropLocation
455 * @since 1.6
456 */
457 public static final class DropLocation extends TransferHandler.DropLocation {
458 private final int row;
459 private final int col;
460 private final boolean isInsertRow;
461 private final boolean isInsertCol;
462
463 private DropLocation(Point p, int row, int col,
464 boolean isInsertRow, boolean isInsertCol) {
465
466 super(p);
467 this.row = row;
468 this.col = col;
469 this.isInsertRow = isInsertRow;
470 this.isInsertCol = isInsertCol;
471 }
472
473 /**
474 * Returns the row index where a dropped item should be placed in the
475 * table. Interpretation of the value depends on the return of
476 * <code>isInsertRow()</code>. If that method returns
477 * <code>true</code> this value indicates the index where a new
478 * row should be inserted. Otherwise, it represents the value
479 * of an existing row on which the data was dropped. This index is
480 * in terms of the view.
481 * <p>
482 * <code>-1</code> indicates that the drop occurred over empty space,
483 * and no row could be calculated.
484 *
485 * @return the drop row
486 */
487 public int getRow() {
488 return row;
489 }
490
491 /**
492 * Returns the column index where a dropped item should be placed in the
493 * table. Interpretation of the value depends on the return of
494 * <code>isInsertColumn()</code>. If that method returns
495 * <code>true</code> this value indicates the index where a new
496 * column should be inserted. Otherwise, it represents the value
497 * of an existing column on which the data was dropped. This index is
498 * in terms of the view.
499 * <p>
500 * <code>-1</code> indicates that the drop occurred over empty space,
501 * and no column could be calculated.
502 *
503 * @return the drop row
504 */
505 public int getColumn() {
506 return col;
507 }
508
509 /**
510 * Returns whether or not this location represents an insert
511 * of a row.
512 *
513 * @return whether or not this is an insert row
514 */
515 public boolean isInsertRow() {
516 return isInsertRow;
517 }
518
519 /**
520 * Returns whether or not this location represents an insert
521 * of a column.
522 *
523 * @return whether or not this is an insert column
524 */
525 public boolean isInsertColumn() {
526 return isInsertCol;
527 }
528
529 /**
530 * Returns a string representation of this drop location.
531 * This method is intended to be used for debugging purposes,
532 * and the content and format of the returned string may vary
533 * between implementations.
534 *
535 * @return a string representation of this drop location
536 */
537 public String toString() {
538 return getClass().getName()
539 + "[dropPoint=" + getDropPoint() + ","
540 + "row=" + row + ","
541 + "column=" + col + ","
542 + "insertRow=" + isInsertRow + ","
543 + "insertColumn=" + isInsertCol + "]";
544 }
545 }
546
547 //
548 // Constructors
549 //
550
551 /**
552 * Constructs a default <code>JTable</code> that is initialized with a default
553 * data model, a default column model, and a default selection
554 * model.
555 *
556 * @see #createDefaultDataModel
557 * @see #createDefaultColumnModel
558 * @see #createDefaultSelectionModel
559 */
560 public JTable() {
561 this(null, null, null);
562 }
563
564 /**
565 * Constructs a <code>JTable</code> that is initialized with
566 * <code>dm</code> as the data model, a default column model,
567 * and a default selection model.
568 *
569 * @param dm the data model for the table
570 * @see #createDefaultColumnModel
571 * @see #createDefaultSelectionModel
572 */
573 public JTable(TableModel dm) {
574 this(dm, null, null);
575 }
576
577 /**
578 * Constructs a <code>JTable</code> that is initialized with
579 * <code>dm</code> as the data model, <code>cm</code>
580 * as the column model, and a default selection model.
581 *
582 * @param dm the data model for the table
583 * @param cm the column model for the table
584 * @see #createDefaultSelectionModel
585 */
586 public JTable(TableModel dm, TableColumnModel cm) {
587 this(dm, cm, null);
588 }
589
590 /**
591 * Constructs a <code>JTable</code> that is initialized with
592 * <code>dm</code> as the data model, <code>cm</code> as the
593 * column model, and <code>sm</code> as the selection model.
594 * If any of the parameters are <code>null</code> this method
595 * will initialize the table with the corresponding default model.
596 * The <code>autoCreateColumnsFromModel</code> flag is set to false
597 * if <code>cm</code> is non-null, otherwise it is set to true
598 * and the column model is populated with suitable
599 * <code>TableColumns</code> for the columns in <code>dm</code>.
600 *
601 * @param dm the data model for the table
602 * @param cm the column model for the table
603 * @param sm the row selection model for the table
604 * @see #createDefaultDataModel
605 * @see #createDefaultColumnModel
606 * @see #createDefaultSelectionModel
607 */
608 public JTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
609 super();
610 setLayout(null);
611
612 setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
613 JComponent.getManagingFocusForwardTraversalKeys());
614 setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
615 JComponent.getManagingFocusBackwardTraversalKeys());
616 if (cm == null) {
617 cm = createDefaultColumnModel();
618 autoCreateColumnsFromModel = true;
619 }
620 setColumnModel(cm);
621
622 if (sm == null) {
623 sm = createDefaultSelectionModel();
624 }
625 setSelectionModel(sm);
626
627 // Set the model last, that way if the autoCreatColumnsFromModel has
628 // been set above, we will automatically populate an empty columnModel
629 // with suitable columns for the new model.
630 if (dm == null) {
631 dm = createDefaultDataModel();
632 }
633 setModel(dm);
634
635 initializeLocalVars();
636 updateUI();
637 }
638
639 /**
640 * Constructs a <code>JTable</code> with <code>numRows</code>
641 * and <code>numColumns</code> of empty cells using
642 * <code>DefaultTableModel</code>. The columns will have
643 * names of the form "A", "B", "C", etc.
644 *
645 * @param numRows the number of rows the table holds
646 * @param numColumns the number of columns the table holds
647 * @see javax.swing.table.DefaultTableModel
648 */
649 public JTable(int numRows, int numColumns) {
650 this(new DefaultTableModel(numRows, numColumns));
651 }
652
653 /**
654 * Constructs a <code>JTable</code> to display the values in the
655 * <code>Vector</code> of <code>Vectors</code>, <code>rowData</code>,
656 * with column names, <code>columnNames</code>. The
657 * <code>Vectors</code> contained in <code>rowData</code>
658 * should contain the values for that row. In other words,
659 * the value of the cell at row 1, column 5 can be obtained
660 * with the following code:
661 * <p>
662 * <pre>((Vector)rowData.elementAt(1)).elementAt(5);</pre>
663 * <p>
664 * @param rowData the data for the new table
665 * @param columnNames names of each column
666 */
667 public JTable(Vector rowData, Vector columnNames) {
668 this(new DefaultTableModel(rowData, columnNames));
669 }
670
671 /**
672 * Constructs a <code>JTable</code> to display the values in the two dimensional array,
673 * <code>rowData</code>, with column names, <code>columnNames</code>.
674 * <code>rowData</code> is an array of rows, so the value of the cell at row 1,
675 * column 5 can be obtained with the following code:
676 * <p>
677 * <pre> rowData[1][5]; </pre>
678 * <p>
679 * All rows must be of the same length as <code>columnNames</code>.
680 * <p>
681 * @param rowData the data for the new table
682 * @param columnNames names of each column
683 */
684 public JTable(final Object[][] rowData, final Object[] columnNames) {
685 this(new AbstractTableModel() {
686 public String getColumnName(int column) { return columnNames[column].toString(); }
687 public int getRowCount() { return rowData.length; }
688 public int getColumnCount() { return columnNames.length; }
689 public Object getValueAt(int row, int col) { return rowData[row][col]; }
690 public boolean isCellEditable(int row, int column) { return true; }
691 public void setValueAt(Object value, int row, int col) {
692 rowData[row][col] = value;
693 fireTableCellUpdated(row, col);
694 }
695 });
696 }
697
698 /**
699 * Calls the <code>configureEnclosingScrollPane</code> method.
700 *
701 * @see #configureEnclosingScrollPane
702 */
703 public void addNotify() {
704 super.addNotify();
705 configureEnclosingScrollPane();
706 }
707
708 /**
709 * If this <code>JTable</code> is the <code>viewportView</code> of an enclosing <code>JScrollPane</code>
710 * (the usual situation), configure this <code>ScrollPane</code> by, amongst other things,
711 * installing the table's <code>tableHeader</code> as the <code>columnHeaderView</code> of the scroll pane.
712 * When a <code>JTable</code> is added to a <code>JScrollPane</code> in the usual way,
713 * using <code>new JScrollPane(myTable)</code>, <code>addNotify</code> is
714 * called in the <code>JTable</code> (when the table is added to the viewport).
715 * <code>JTable</code>'s <code>addNotify</code> method in turn calls this method,
716 * which is protected so that this default installation procedure can
717 * be overridden by a subclass.
718 *
719 * @see #addNotify
720 */
721 protected void configureEnclosingScrollPane() {
722 Container parent = SwingUtilities.getUnwrappedParent(this);
723 if (parent instanceof JViewport) {
724 JViewport port = (JViewport) parent;
725 Container gp = port.getParent();
726 if (gp instanceof JScrollPane) {
727 JScrollPane scrollPane = (JScrollPane)gp;
728 // Make certain we are the viewPort's view and not, for
729 // example, the rowHeaderView of the scrollPane -
730 // an implementor of fixed columns might do this.
731 JViewport viewport = scrollPane.getViewport();
732 if (viewport == null ||
733 SwingUtilities.getUnwrappedView(viewport) != this) {
734 return;
735 }
736 scrollPane.setColumnHeaderView(getTableHeader());
737 // configure the scrollpane for any LAF dependent settings
738 configureEnclosingScrollPaneUI();
739 }
740 }
741 }
742
743 /**
744 * This is a sub-part of configureEnclosingScrollPane() that configures
745 * anything on the scrollpane that may change when the look and feel
746 * changes. It needed to be split out from configureEnclosingScrollPane() so
747 * that it can be called from updateUI() when the LAF changes without
748 * causing the regression found in bug 6687962. This was because updateUI()
749 * is called from the constructor which then caused
750 * configureEnclosingScrollPane() to be called by the constructor which
751 * changes its contract for any subclass that overrides it. So by splitting
752 * it out in this way configureEnclosingScrollPaneUI() can be called both
753 * from configureEnclosingScrollPane() and updateUI() in a safe manor.
754 */
755 private void configureEnclosingScrollPaneUI() {
756 Container parent = SwingUtilities.getUnwrappedParent(this);
757 if (parent instanceof JViewport) {
758 JViewport port = (JViewport) parent;
759 Container gp = port.getParent();
760 if (gp instanceof JScrollPane) {
761 JScrollPane scrollPane = (JScrollPane)gp;
762 // Make certain we are the viewPort's view and not, for
763 // example, the rowHeaderView of the scrollPane -
764 // an implementor of fixed columns might do this.
765 JViewport viewport = scrollPane.getViewport();
766 if (viewport == null ||
767 SwingUtilities.getUnwrappedView(viewport) != this) {
768 return;
769 }
770 // scrollPane.getViewport().setBackingStoreEnabled(true);
771 Border border = scrollPane.getBorder();
772 if (border == null || border instanceof UIResource) {
773 Border scrollPaneBorder =
774 UIManager.getBorder("Table.scrollPaneBorder");
775 if (scrollPaneBorder != null) {
776 scrollPane.setBorder(scrollPaneBorder);
777 }
778 }
779 // add JScrollBar corner component if available from LAF and not already set by the user
780 Component corner =
781 scrollPane.getCorner(JScrollPane.UPPER_TRAILING_CORNER);
782 if (corner == null || corner instanceof UIResource){
783 corner = null;
784 Object componentClass = UIManager.get(
785 "Table.scrollPaneCornerComponent");
786 if (componentClass instanceof Class){
787 try {
788 corner = (Component)
789 ((Class)componentClass).newInstance();
790 } catch (Exception e) {
791 // just ignore and don't set corner
792 }
793 }
794 scrollPane.setCorner(JScrollPane.UPPER_TRAILING_CORNER,
795 corner);
796 }
797 }
798 }
799 }
800
801 /**
802 * Calls the <code>unconfigureEnclosingScrollPane</code> method.
803 *
804 * @see #unconfigureEnclosingScrollPane
805 */
806 public void removeNotify() {
807 KeyboardFocusManager.getCurrentKeyboardFocusManager().
808 removePropertyChangeListener("permanentFocusOwner", editorRemover);
809 editorRemover = null;
810 unconfigureEnclosingScrollPane();
811 super.removeNotify();
812 }
813
814 /**
815 * Reverses the effect of <code>configureEnclosingScrollPane</code>
816 * by replacing the <code>columnHeaderView</code> of the enclosing
817 * scroll pane with <code>null</code>. <code>JTable</code>'s
818 * <code>removeNotify</code> method calls
819 * this method, which is protected so that this default uninstallation
820 * procedure can be overridden by a subclass.
821 *
822 * @see #removeNotify
823 * @see #configureEnclosingScrollPane
824 * @since 1.3
825 */
826 protected void unconfigureEnclosingScrollPane() {
827 Container parent = SwingUtilities.getUnwrappedParent(this);
828 if (parent instanceof JViewport) {
829 JViewport port = (JViewport) parent;
830 Container gp = port.getParent();
831 if (gp instanceof JScrollPane) {
832 JScrollPane scrollPane = (JScrollPane)gp;
833 // Make certain we are the viewPort's view and not, for
834 // example, the rowHeaderView of the scrollPane -
835 // an implementor of fixed columns might do this.
836 JViewport viewport = scrollPane.getViewport();
837 if (viewport == null ||
838 SwingUtilities.getUnwrappedView(viewport) != this) {
839 return;
840 }
841 scrollPane.setColumnHeaderView(null);
842 // remove ScrollPane corner if one was added by the LAF
843 Component corner =
844 scrollPane.getCorner(JScrollPane.UPPER_TRAILING_CORNER);
845 if (corner instanceof UIResource){
846 scrollPane.setCorner(JScrollPane.UPPER_TRAILING_CORNER,
847 null);
848 }
849 }
850 }
851 }
852
853 void setUIProperty(String propertyName, Object value) {
854 if (propertyName == "rowHeight") {
855 if (!isRowHeightSet) {
856 setRowHeight(((Number)value).intValue());
857 isRowHeightSet = false;
858 }
859 return;
860 }
861 super.setUIProperty(propertyName, value);
862 }
863
864 //
865 // Static Methods
866 //
867
868 /**
869 * Equivalent to <code>new JScrollPane(aTable)</code>.
870 *
871 * @deprecated As of Swing version 1.0.2,
872 * replaced by <code>new JScrollPane(aTable)</code>.
873 */
874 @Deprecated
875 static public JScrollPane createScrollPaneForTable(JTable aTable) {
876 return new JScrollPane(aTable);
877 }
878
879 //
880 // Table Attributes
881 //
882
883 /**
884 * Sets the <code>tableHeader</code> working with this <code>JTable</code> to <code>newHeader</code>.
885 * It is legal to have a <code>null</code> <code>tableHeader</code>.
886 *
887 * @param tableHeader new tableHeader
888 * @see #getTableHeader
889 * @beaninfo
890 * bound: true
891 * description: The JTableHeader instance which renders the column headers.
892 */
893 public void setTableHeader(JTableHeader tableHeader) {
894 if (this.tableHeader != tableHeader) {
895 JTableHeader old = this.tableHeader;
896 // Release the old header
897 if (old != null) {
898 old.setTable(null);
899 }
900 this.tableHeader = tableHeader;
901 if (tableHeader != null) {
902 tableHeader.setTable(this);
903 }
904 firePropertyChange("tableHeader", old, tableHeader);
905 }
906 }
907
908 /**
909 * Returns the <code>tableHeader</code> used by this <code>JTable</code>.
910 *
911 * @return the <code>tableHeader</code> used by this table
912 * @see #setTableHeader
913 */
914 public JTableHeader getTableHeader() {
915 return tableHeader;
916 }
917
918 /**
919 * Sets the height, in pixels, of all cells to <code>rowHeight</code>,
920 * revalidates, and repaints.
921 * The height of the cells will be equal to the row height minus
922 * the row margin.
923 *
924 * @param rowHeight new row height
925 * @exception IllegalArgumentException if <code>rowHeight</code> is
926 * less than 1
927 * @see #getRowHeight
928 * @beaninfo
929 * bound: true
930 * description: The height of the specified row.
931 */
932 public void setRowHeight(int rowHeight) {
933 if (rowHeight <= 0) {
934 throw new IllegalArgumentException("New row height less than 1");
935 }
936 int old = this.rowHeight;
937 this.rowHeight = rowHeight;
938 rowModel = null;
939 if (sortManager != null) {
940 sortManager.modelRowSizes = null;
941 }
942 isRowHeightSet = true;
943 resizeAndRepaint();
944 firePropertyChange("rowHeight", old, rowHeight);
945 }
946
947 /**
948 * Returns the height of a table row, in pixels.
949 *
950 * @return the height in pixels of a table row
951 * @see #setRowHeight
952 */
953 public int getRowHeight() {
954 return rowHeight;
955 }
956
957 private SizeSequence getRowModel() {
958 if (rowModel == null) {
959 rowModel = new SizeSequence(getRowCount(), getRowHeight());
960 }
961 return rowModel;
962 }
963
964 /**
965 * Sets the height for <code>row</code> to <code>rowHeight</code>,
966 * revalidates, and repaints. The height of the cells in this row
967 * will be equal to the row height minus the row margin.
968 *
969 * @param row the row whose height is being
970 changed
971 * @param rowHeight new row height, in pixels
972 * @exception IllegalArgumentException if <code>rowHeight</code> is
973 * less than 1
974 * @beaninfo
975 * bound: true
976 * description: The height in pixels of the cells in <code>row</code>
977 * @since 1.3
978 */
979 public void setRowHeight(int row, int rowHeight) {
980 if (rowHeight <= 0) {
981 throw new IllegalArgumentException("New row height less than 1");
982 }
983 getRowModel().setSize(row, rowHeight);
984 if (sortManager != null) {
985 sortManager.setViewRowHeight(row, rowHeight);
986 }
987 resizeAndRepaint();
988 }
989
990 /**
991 * Returns the height, in pixels, of the cells in <code>row</code>.
992 * @param row the row whose height is to be returned
993 * @return the height, in pixels, of the cells in the row
994 * @since 1.3
995 */
996 public int getRowHeight(int row) {
997 return (rowModel == null) ? getRowHeight() : rowModel.getSize(row);
998 }
999
1000 /**
1001 * Sets the amount of empty space between cells in adjacent rows.
1002 *
1003 * @param rowMargin the number of pixels between cells in a row
1004 * @see #getRowMargin
1005 * @beaninfo
1006 * bound: true
1007 * description: The amount of space between cells.
1008 */
1009 public void setRowMargin(int rowMargin) {
1010 int old = this.rowMargin;
1011 this.rowMargin = rowMargin;
1012 resizeAndRepaint();
1013 firePropertyChange("rowMargin", old, rowMargin);
1014 }
1015
1016 /**
1017 * Gets the amount of empty space, in pixels, between cells. Equivalent to:
1018 * <code>getIntercellSpacing().height</code>.
1019 * @return the number of pixels between cells in a row
1020 *
1021 * @see #setRowMargin
1022 */
1023 public int getRowMargin() {
1024 return rowMargin;
1025 }
1026
1027 /**
1028 * Sets the <code>rowMargin</code> and the <code>columnMargin</code> --
1029 * the height and width of the space between cells -- to
1030 * <code>intercellSpacing</code>.
1031 *
1032 * @param intercellSpacing a <code>Dimension</code>
1033 * specifying the new width
1034 * and height between cells
1035 * @see #getIntercellSpacing
1036 * @beaninfo
1037 * description: The spacing between the cells,
1038 * drawn in the background color of the JTable.
1039 */
1040 public void setIntercellSpacing(Dimension intercellSpacing) {
1041 // Set the rowMargin here and columnMargin in the TableColumnModel
1042 setRowMargin(intercellSpacing.height);
1043 getColumnModel().setColumnMargin(intercellSpacing.width);
1044
1045 resizeAndRepaint();
1046 }
1047
1048 /**
1049 * Returns the horizontal and vertical space between cells.
1050 * The default spacing is look and feel dependent.
1051 *
1052 * @return the horizontal and vertical spacing between cells
1053 * @see #setIntercellSpacing
1054 */
1055 public Dimension getIntercellSpacing() {
1056 return new Dimension(getColumnModel().getColumnMargin(), rowMargin);
1057 }
1058
1059 /**
1060 * Sets the color used to draw grid lines to <code>gridColor</code> and redisplays.
1061 * The default color is look and feel dependent.
1062 *
1063 * @param gridColor the new color of the grid lines
1064 * @exception IllegalArgumentException if <code>gridColor</code> is <code>null</code>
1065 * @see #getGridColor
1066 * @beaninfo
1067 * bound: true
1068 * description: The grid color.
1069 */
1070 public void setGridColor(Color gridColor) {
1071 if (gridColor == null) {
1072 throw new IllegalArgumentException("New color is null");
1073 }
1074 Color old = this.gridColor;
1075 this.gridColor = gridColor;
1076 firePropertyChange("gridColor", old, gridColor);
1077 // Redraw
1078 repaint();
1079 }
1080
1081 /**
1082 * Returns the color used to draw grid lines.
1083 * The default color is look and feel dependent.
1084 *
1085 * @return the color used to draw grid lines
1086 * @see #setGridColor
1087 */
1088 public Color getGridColor() {
1089 return gridColor;
1090 }
1091
1092 /**
1093 * Sets whether the table draws grid lines around cells.
1094 * If <code>showGrid</code> is true it does; if it is false it doesn't.
1095 * There is no <code>getShowGrid</code> method as this state is held
1096 * in two variables -- <code>showHorizontalLines</code> and <code>showVerticalLines</code> --
1097 * each of which can be queried independently.
1098 *
1099 * @param showGrid true if table view should draw grid lines
1100 *
1101 * @see #setShowVerticalLines
1102 * @see #setShowHorizontalLines
1103 * @beaninfo
1104 * description: The color used to draw the grid lines.
1105 */
1106 public void setShowGrid(boolean showGrid) {
1107 setShowHorizontalLines(showGrid);
1108 setShowVerticalLines(showGrid);
1109
1110 // Redraw
1111 repaint();
1112 }
1113
1114 /**
1115 * Sets whether the table draws horizontal lines between cells.
1116 * If <code>showHorizontalLines</code> is true it does; if it is false it doesn't.
1117 *
1118 * @param showHorizontalLines true if table view should draw horizontal lines
1119 * @see #getShowHorizontalLines
1120 * @see #setShowGrid
1121 * @see #setShowVerticalLines
1122 * @beaninfo
1123 * bound: true
1124 * description: Whether horizontal lines should be drawn in between the cells.
1125 */
1126 public void setShowHorizontalLines(boolean showHorizontalLines) {
1127 boolean old = this.showHorizontalLines;
1128 this.showHorizontalLines = showHorizontalLines;
1129 firePropertyChange("showHorizontalLines", old, showHorizontalLines);
1130
1131 // Redraw
1132 repaint();
1133 }
1134
1135 /**
1136 * Sets whether the table draws vertical lines between cells.
1137 * If <code>showVerticalLines</code> is true it does; if it is false it doesn't.
1138 *
1139 * @param showVerticalLines true if table view should draw vertical lines
1140 * @see #getShowVerticalLines
1141 * @see #setShowGrid
1142 * @see #setShowHorizontalLines
1143 * @beaninfo
1144 * bound: true
1145 * description: Whether vertical lines should be drawn in between the cells.
1146 */
1147 public void setShowVerticalLines(boolean showVerticalLines) {
1148 boolean old = this.showVerticalLines;
1149 this.showVerticalLines = showVerticalLines;
1150 firePropertyChange("showVerticalLines", old, showVerticalLines);
1151 // Redraw
1152 repaint();
1153 }
1154
1155 /**
1156 * Returns true if the table draws horizontal lines between cells, false if it
1157 * doesn't. The default value is look and feel dependent.
1158 *
1159 * @return true if the table draws horizontal lines between cells, false if it
1160 * doesn't
1161 * @see #setShowHorizontalLines
1162 */
1163 public boolean getShowHorizontalLines() {
1164 return showHorizontalLines;
1165 }
1166
1167 /**
1168 * Returns true if the table draws vertical lines between cells, false if it
1169 * doesn't. The default value is look and feel dependent.
1170 *
1171 * @return true if the table draws vertical lines between cells, false if it
1172 * doesn't
1173 * @see #setShowVerticalLines
1174 */
1175 public boolean getShowVerticalLines() {
1176 return showVerticalLines;
1177 }
1178
1179 /**
1180 * Sets the table's auto resize mode when the table is resized. For further
1181 * information on how the different resize modes work, see
1182 * {@link #doLayout}.
1183 *
1184 * @param mode One of 5 legal values:
1185 * AUTO_RESIZE_OFF,
1186 * AUTO_RESIZE_NEXT_COLUMN,
1187 * AUTO_RESIZE_SUBSEQUENT_COLUMNS,
1188 * AUTO_RESIZE_LAST_COLUMN,
1189 * AUTO_RESIZE_ALL_COLUMNS
1190 *
1191 * @see #getAutoResizeMode
1192 * @see #doLayout
1193 * @beaninfo
1194 * bound: true
1195 * description: Whether the columns should adjust themselves automatically.
1196 * enum: AUTO_RESIZE_OFF JTable.AUTO_RESIZE_OFF
1197 * AUTO_RESIZE_NEXT_COLUMN JTable.AUTO_RESIZE_NEXT_COLUMN
1198 * AUTO_RESIZE_SUBSEQUENT_COLUMNS JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS
1199 * AUTO_RESIZE_LAST_COLUMN JTable.AUTO_RESIZE_LAST_COLUMN
1200 * AUTO_RESIZE_ALL_COLUMNS JTable.AUTO_RESIZE_ALL_COLUMNS
1201 */
1202 public void setAutoResizeMode(int mode) {
1203 if ((mode == AUTO_RESIZE_OFF) ||
1204 (mode == AUTO_RESIZE_NEXT_COLUMN) ||
1205 (mode == AUTO_RESIZE_SUBSEQUENT_COLUMNS) ||
1206 (mode == AUTO_RESIZE_LAST_COLUMN) ||
1207 (mode == AUTO_RESIZE_ALL_COLUMNS)) {
1208 int old = autoResizeMode;
1209 autoResizeMode = mode;
1210 resizeAndRepaint();
1211 if (tableHeader != null) {
1212 tableHeader.resizeAndRepaint();
1213 }
1214 firePropertyChange("autoResizeMode", old, autoResizeMode);
1215 }
1216 }
1217
1218 /**
1219 * Returns the auto resize mode of the table. The default mode
1220 * is AUTO_RESIZE_SUBSEQUENT_COLUMNS.
1221 *
1222 * @return the autoResizeMode of the table
1223 *
1224 * @see #setAutoResizeMode
1225 * @see #doLayout
1226 */
1227 public int getAutoResizeMode() {
1228 return autoResizeMode;
1229 }
1230
1231 /**
1232 * Sets this table's <code>autoCreateColumnsFromModel</code> flag.
1233 * This method calls <code>createDefaultColumnsFromModel</code> if
1234 * <code>autoCreateColumnsFromModel</code> changes from false to true.
1235 *
1236 * @param autoCreateColumnsFromModel true if <code>JTable</code> should automatically create columns
1237 * @see #getAutoCreateColumnsFromModel
1238 * @see #createDefaultColumnsFromModel
1239 * @beaninfo
1240 * bound: true
1241 * description: Automatically populates the columnModel when a new TableModel is submitted.
1242 */
1243 public void setAutoCreateColumnsFromModel(boolean autoCreateColumnsFromModel) {
1244 if (this.autoCreateColumnsFromModel != autoCreateColumnsFromModel) {
1245 boolean old = this.autoCreateColumnsFromModel;
1246 this.autoCreateColumnsFromModel = autoCreateColumnsFromModel;
1247 if (autoCreateColumnsFromModel) {
1248 createDefaultColumnsFromModel();
1249 }
1250 firePropertyChange("autoCreateColumnsFromModel", old, autoCreateColumnsFromModel);
1251 }
1252 }
1253
1254 /**
1255 * Determines whether the table will create default columns from the model.
1256 * If true, <code>setModel</code> will clear any existing columns and
1257 * create new columns from the new model. Also, if the event in
1258 * the <code>tableChanged</code> notification specifies that the
1259 * entire table changed, then the columns will be rebuilt.
1260 * The default is true.
1261 *
1262 * @return the autoCreateColumnsFromModel of the table
1263 * @see #setAutoCreateColumnsFromModel
1264 * @see #createDefaultColumnsFromModel
1265 */
1266 public boolean getAutoCreateColumnsFromModel() {
1267 return autoCreateColumnsFromModel;
1268 }
1269
1270 /**
1271 * Creates default columns for the table from
1272 * the data model using the <code>getColumnCount</code> method
1273 * defined in the <code>TableModel</code> interface.
1274 * <p>
1275 * Clears any existing columns before creating the
1276 * new columns based on information from the model.
1277 *
1278 * @see #getAutoCreateColumnsFromModel
1279 */
1280 public void createDefaultColumnsFromModel() {
1281 TableModel m = getModel();
1282 if (m != null) {
1283 // Remove any current columns
1284 TableColumnModel cm = getColumnModel();
1285 while (cm.getColumnCount() > 0) {
1286 cm.removeColumn(cm.getColumn(0));
1287 }
1288
1289 // Create new columns from the data model info
1290 for (int i = 0; i < m.getColumnCount(); i++) {
1291 TableColumn newColumn = new TableColumn(i);
1292 addColumn(newColumn);
1293 }
1294 }
1295 }
1296
1297 /**
1298 * Sets a default cell renderer to be used if no renderer has been set in
1299 * a <code>TableColumn</code>. If renderer is <code>null</code>,
1300 * removes the default renderer for this column class.
1301 *
1302 * @param columnClass set the default cell renderer for this columnClass
1303 * @param renderer default cell renderer to be used for this
1304 * columnClass
1305 * @see #getDefaultRenderer
1306 * @see #setDefaultEditor
1307 */
1308 public void setDefaultRenderer(Class<?> columnClass, TableCellRenderer renderer) {
1309 if (renderer != null) {
1310 defaultRenderersByColumnClass.put(columnClass, renderer);
1311 }
1312 else {
1313 defaultRenderersByColumnClass.remove(columnClass);
1314 }
1315 }
1316
1317 /**
1318 * Returns the cell renderer to be used when no renderer has been set in
1319 * a <code>TableColumn</code>. During the rendering of cells the renderer is fetched from
1320 * a <code>Hashtable</code> of entries according to the class of the cells in the column. If
1321 * there is no entry for this <code>columnClass</code> the method returns
1322 * the entry for the most specific superclass. The <code>JTable</code> installs entries
1323 * for <code>Object</code>, <code>Number</code>, and <code>Boolean</code>, all of which can be modified
1324 * or replaced.
1325 *
1326 * @param columnClass return the default cell renderer
1327 * for this columnClass
1328 * @return the renderer for this columnClass
1329 * @see #setDefaultRenderer
1330 * @see #getColumnClass
1331 */
1332 public TableCellRenderer getDefaultRenderer(Class<?> columnClass) {
1333 if (columnClass == null) {
1334 return null;
1335 }
1336 else {
1337 Object renderer = defaultRenderersByColumnClass.get(columnClass);
1338 if (renderer != null) {
1339 return (TableCellRenderer)renderer;
1340 }
1341 else {
1342 Class c = columnClass.getSuperclass();
1343 if (c == null && columnClass != Object.class) {
1344 c = Object.class;
1345 }
1346 return getDefaultRenderer(c);
1347 }
1348 }
1349 }
1350
1351 /**
1352 * Sets a default cell editor to be used if no editor has been set in
1353 * a <code>TableColumn</code>. If no editing is required in a table, or a
1354 * particular column in a table, uses the <code>isCellEditable</code>
1355 * method in the <code>TableModel</code> interface to ensure that this
1356 * <code>JTable</code> will not start an editor in these columns.
1357 * If editor is <code>null</code>, removes the default editor for this
1358 * column class.
1359 *
1360 * @param columnClass set the default cell editor for this columnClass
1361 * @param editor default cell editor to be used for this columnClass
1362 * @see TableModel#isCellEditable
1363 * @see #getDefaultEditor
1364 * @see #setDefaultRenderer
1365 */
1366 public void setDefaultEditor(Class<?> columnClass, TableCellEditor editor) {
1367 if (editor != null) {
1368 defaultEditorsByColumnClass.put(columnClass, editor);
1369 }
1370 else {
1371 defaultEditorsByColumnClass.remove(columnClass);
1372 }
1373 }
1374
1375 /**
1376 * Returns the editor to be used when no editor has been set in
1377 * a <code>TableColumn</code>. During the editing of cells the editor is fetched from
1378 * a <code>Hashtable</code> of entries according to the class of the cells in the column. If
1379 * there is no entry for this <code>columnClass</code> the method returns
1380 * the entry for the most specific superclass. The <code>JTable</code> installs entries
1381 * for <code>Object</code>, <code>Number</code>, and <code>Boolean</code>, all of which can be modified
1382 * or replaced.
1383 *
1384 * @param columnClass return the default cell editor for this columnClass
1385 * @return the default cell editor to be used for this columnClass
1386 * @see #setDefaultEditor
1387 * @see #getColumnClass
1388 */
1389 public TableCellEditor getDefaultEditor(Class<?> columnClass) {
1390 if (columnClass == null) {
1391 return null;
1392 }
1393 else {
1394 Object editor = defaultEditorsByColumnClass.get(columnClass);
1395 if (editor != null) {
1396 return (TableCellEditor)editor;
1397 }
1398 else {
1399 return getDefaultEditor(columnClass.getSuperclass());
1400 }
1401 }
1402 }
1403
1404 /**
1405 * Turns on or off automatic drag handling. In order to enable automatic
1406 * drag handling, this property should be set to {@code true}, and the
1407 * table's {@code TransferHandler} needs to be {@code non-null}.
1408 * The default value of the {@code dragEnabled} property is {@code false}.
1409 * <p>
1410 * The job of honoring this property, and recognizing a user drag gesture,
1411 * lies with the look and feel implementation, and in particular, the table's
1412 * {@code TableUI}. When automatic drag handling is enabled, most look and
1413 * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1414 * drag and drop operation whenever the user presses the mouse button over
1415 * an item (in single selection mode) or a selection (in other selection
1416 * modes) and then moves the mouse a few pixels. Setting this property to
1417 * {@code true} can therefore have a subtle effect on how selections behave.
1418 * <p>
1419 * If a look and feel is used that ignores this property, you can still
1420 * begin a drag and drop operation by calling {@code exportAsDrag} on the
1421 * table's {@code TransferHandler}.
1422 *
1423 * @param b whether or not to enable automatic drag handling
1424 * @exception HeadlessException if
1425 * <code>b</code> is <code>true</code> and
1426 * <code>GraphicsEnvironment.isHeadless()</code>
1427 * returns <code>true</code>
1428 * @see java.awt.GraphicsEnvironment#isHeadless
1429 * @see #getDragEnabled
1430 * @see #setTransferHandler
1431 * @see TransferHandler
1432 * @since 1.4
1433 *
1434 * @beaninfo
1435 * description: determines whether automatic drag handling is enabled
1436 * bound: false
1437 */
1438 public void setDragEnabled(boolean b) {
1439 if (b && GraphicsEnvironment.isHeadless()) {
1440 throw new HeadlessException();
1441 }
1442 dragEnabled = b;
1443 }
1444
1445 /**
1446 * Returns whether or not automatic drag handling is enabled.
1447 *
1448 * @return the value of the {@code dragEnabled} property
1449 * @see #setDragEnabled
1450 * @since 1.4
1451 */
1452 public boolean getDragEnabled() {
1453 return dragEnabled;
1454 }
1455
1456 /**
1457 * Sets the drop mode for this component. For backward compatibility,
1458 * the default for this property is <code>DropMode.USE_SELECTION</code>.
1459 * Usage of one of the other modes is recommended, however, for an
1460 * improved user experience. <code>DropMode.ON</code>, for instance,
1461 * offers similar behavior of showing items as selected, but does so without
1462 * affecting the actual selection in the table.
1463 * <p>
1464 * <code>JTable</code> supports the following drop modes:
1465 * <ul>
1466 * <li><code>DropMode.USE_SELECTION</code></li>
1467 * <li><code>DropMode.ON</code></li>
1468 * <li><code>DropMode.INSERT</code></li>
1469 * <li><code>DropMode.INSERT_ROWS</code></li>
1470 * <li><code>DropMode.INSERT_COLS</code></li>
1471 * <li><code>DropMode.ON_OR_INSERT</code></li>
1472 * <li><code>DropMode.ON_OR_INSERT_ROWS</code></li>
1473 * <li><code>DropMode.ON_OR_INSERT_COLS</code></li>
1474 * </ul>
1475 * <p>
1476 * The drop mode is only meaningful if this component has a
1477 * <code>TransferHandler</code> that accepts drops.
1478 *
1479 * @param dropMode the drop mode to use
1480 * @throws IllegalArgumentException if the drop mode is unsupported
1481 * or <code>null</code>
1482 * @see #getDropMode
1483 * @see #getDropLocation
1484 * @see #setTransferHandler
1485 * @see TransferHandler
1486 * @since 1.6
1487 */
1488 public final void setDropMode(DropMode dropMode) {
1489 if (dropMode != null) {
1490 switch (dropMode) {
1491 case USE_SELECTION:
1492 case ON:
1493 case INSERT:
1494 case INSERT_ROWS:
1495 case INSERT_COLS:
1496 case ON_OR_INSERT:
1497 case ON_OR_INSERT_ROWS:
1498 case ON_OR_INSERT_COLS:
1499 this.dropMode = dropMode;
1500 return;
1501 }
1502 }
1503
1504 throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for table");
1505 }
1506
1507 /**
1508 * Returns the drop mode for this component.
1509 *
1510 * @return the drop mode for this component
1511 * @see #setDropMode
1512 * @since 1.6
1513 */
1514 public final DropMode getDropMode() {
1515 return dropMode;
1516 }
1517
1518 /**
1519 * Calculates a drop location in this component, representing where a
1520 * drop at the given point should insert data.
1521 *
1522 * @param p the point to calculate a drop location for
1523 * @return the drop location, or <code>null</code>
1524 */
1525 DropLocation dropLocationForPoint(Point p) {
1526 DropLocation location = null;
1527
1528 int row = rowAtPoint(p);
1529 int col = columnAtPoint(p);
1530 boolean outside = Boolean.TRUE == getClientProperty("Table.isFileList")
1531 && SwingUtilities2.pointOutsidePrefSize(this, row, col, p);
1532
1533 Rectangle rect = getCellRect(row, col, true);
1534 Section xSection, ySection;
1535 boolean between = false;
1536 boolean ltr = getComponentOrientation().isLeftToRight();
1537
1538 switch(dropMode) {
1539 case USE_SELECTION:
1540 case ON:
1541 if (row == -1 || col == -1 || outside) {
1542 location = new DropLocation(p, -1, -1, false, false);
1543 } else {
1544 location = new DropLocation(p, row, col, false, false);
1545 }
1546 break;
1547 case INSERT:
1548 if (row == -1 && col == -1) {
1549 location = new DropLocation(p, 0, 0, true, true);
1550 break;
1551 }
1552
1553 xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1554
1555 if (row == -1) {
1556 if (xSection == LEADING) {
1557 location = new DropLocation(p, getRowCount(), col, true, true);
1558 } else if (xSection == TRAILING) {
1559 location = new DropLocation(p, getRowCount(), col + 1, true, true);
1560 } else {
1561 location = new DropLocation(p, getRowCount(), col, true, false);
1562 }
1563 } else if (xSection == LEADING || xSection == TRAILING) {
1564 ySection = SwingUtilities2.liesInVertical(rect, p, true);
1565 if (ySection == LEADING) {
1566 between = true;
1567 } else if (ySection == TRAILING) {
1568 row++;
1569 between = true;
1570 }
1571
1572 location = new DropLocation(p, row,
1573 xSection == TRAILING ? col + 1 : col,
1574 between, true);
1575 } else {
1576 if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) {
1577 row++;
1578 }
1579
1580 location = new DropLocation(p, row, col, true, false);
1581 }
1582
1583 break;
1584 case INSERT_ROWS:
1585 if (row == -1 && col == -1) {
1586 location = new DropLocation(p, -1, -1, false, false);
1587 break;
1588 }
1589
1590 if (row == -1) {
1591 location = new DropLocation(p, getRowCount(), col, true, false);
1592 break;
1593 }
1594
1595 if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) {
1596 row++;
1597 }
1598
1599 location = new DropLocation(p, row, col, true, false);
1600 break;
1601 case ON_OR_INSERT_ROWS:
1602 if (row == -1 && col == -1) {
1603 location = new DropLocation(p, -1, -1, false, false);
1604 break;
1605 }
1606
1607 if (row == -1) {
1608 location = new DropLocation(p, getRowCount(), col, true, false);
1609 break;
1610 }
1611
1612 ySection = SwingUtilities2.liesInVertical(rect, p, true);
1613 if (ySection == LEADING) {
1614 between = true;
1615 } else if (ySection == TRAILING) {
1616 row++;
1617 between = true;
1618 }
1619
1620 location = new DropLocation(p, row, col, between, false);
1621 break;
1622 case INSERT_COLS:
1623 if (row == -1) {
1624 location = new DropLocation(p, -1, -1, false, false);
1625 break;
1626 }
1627
1628 if (col == -1) {
1629 location = new DropLocation(p, getColumnCount(), col, false, true);
1630 break;
1631 }
1632
1633 if (SwingUtilities2.liesInHorizontal(rect, p, ltr, false) == TRAILING) {
1634 col++;
1635 }
1636
1637 location = new DropLocation(p, row, col, false, true);
1638 break;
1639 case ON_OR_INSERT_COLS:
1640 if (row == -1) {
1641 location = new DropLocation(p, -1, -1, false, false);
1642 break;
1643 }
1644
1645 if (col == -1) {
1646 location = new DropLocation(p, row, getColumnCount(), false, true);
1647 break;
1648 }
1649
1650 xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1651 if (xSection == LEADING) {
1652 between = true;
1653 } else if (xSection == TRAILING) {
1654 col++;
1655 between = true;
1656 }
1657
1658 location = new DropLocation(p, row, col, false, between);
1659 break;
1660 case ON_OR_INSERT:
1661 if (row == -1 && col == -1) {
1662 location = new DropLocation(p, 0, 0, true, true);
1663 break;
1664 }
1665
1666 xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1667
1668 if (row == -1) {
1669 if (xSection == LEADING) {
1670 location = new DropLocation(p, getRowCount(), col, true, true);
1671 } else if (xSection == TRAILING) {
1672 location = new DropLocation(p, getRowCount(), col + 1, true, true);
1673 } else {
1674 location = new DropLocation(p, getRowCount(), col, true, false);
1675 }
1676
1677 break;
1678 }
1679
1680 ySection = SwingUtilities2.liesInVertical(rect, p, true);
1681 if (ySection == LEADING) {
1682 between = true;
1683 } else if (ySection == TRAILING) {
1684 row++;
1685 between = true;
1686 }
1687
1688 location = new DropLocation(p, row,
1689 xSection == TRAILING ? col + 1 : col,
1690 between,
1691 xSection != MIDDLE);
1692
1693 break;
1694 default:
1695 assert false : "Unexpected drop mode";
1696 }
1697
1698 return location;
1699 }
1700
1701 /**
1702 * Called to set or clear the drop location during a DnD operation.
1703 * In some cases, the component may need to use it's internal selection
1704 * temporarily to indicate the drop location. To help facilitate this,
1705 * this method returns and accepts as a parameter a state object.
1706 * This state object can be used to store, and later restore, the selection
1707 * state. Whatever this method returns will be passed back to it in
1708 * future calls, as the state parameter. If it wants the DnD system to
1709 * continue storing the same state, it must pass it back every time.
1710 * Here's how this is used:
1711 * <p>
1712 * Let's say that on the first call to this method the component decides
1713 * to save some state (because it is about to use the selection to show
1714 * a drop index). It can return a state object to the caller encapsulating
1715 * any saved selection state. On a second call, let's say the drop location
1716 * is being changed to something else. The component doesn't need to
1717 * restore anything yet, so it simply passes back the same state object
1718 * to have the DnD system continue storing it. Finally, let's say this
1719 * method is messaged with <code>null</code>. This means DnD
1720 * is finished with this component for now, meaning it should restore
1721 * state. At this point, it can use the state parameter to restore
1722 * said state, and of course return <code>null</code> since there's
1723 * no longer anything to store.
1724 *
1725 * @param location the drop location (as calculated by
1726 * <code>dropLocationForPoint</code>) or <code>null</code>
1727 * if there's no longer a valid drop location
1728 * @param state the state object saved earlier for this component,
1729 * or <code>null</code>
1730 * @param forDrop whether or not the method is being called because an
1731 * actual drop occurred
1732 * @return any saved state for this component, or <code>null</code> if none
1733 */
1734 Object setDropLocation(TransferHandler.DropLocation location,
1735 Object state,
1736 boolean forDrop) {
1737
1738 Object retVal = null;
1739 DropLocation tableLocation = (DropLocation)location;
1740
1741 if (dropMode == DropMode.USE_SELECTION) {
1742 if (tableLocation == null) {
1743 if (!forDrop && state != null) {
1744 clearSelection();
1745
1746 int[] rows = ((int[][])state)[0];
1747 int[] cols = ((int[][])state)[1];
1748 int[] anchleads = ((int[][])state)[2];
1749
1750 for (int row : rows) {
1751 addRowSelectionInterval(row, row);
1752 }
1753
1754 for (int col : cols) {
1755 addColumnSelectionInterval(col, col);
1756 }
1757
1758 SwingUtilities2.setLeadAnchorWithoutSelection(
1759 getSelectionModel(), anchleads[1], anchleads[0]);
1760
1761 SwingUtilities2.setLeadAnchorWithoutSelection(
1762 getColumnModel().getSelectionModel(),
1763 anchleads[3], anchleads[2]);
1764 }
1765 } else {
1766 if (dropLocation == null) {
1767 retVal = new int[][]{
1768 getSelectedRows(),
1769 getSelectedColumns(),
1770 {getAdjustedIndex(getSelectionModel()
1771 .getAnchorSelectionIndex(), true),
1772 getAdjustedIndex(getSelectionModel()
1773 .getLeadSelectionIndex(), true),
1774 getAdjustedIndex(getColumnModel().getSelectionModel()
1775 .getAnchorSelectionIndex(), false),
1776 getAdjustedIndex(getColumnModel().getSelectionModel()
1777 .getLeadSelectionIndex(), false)}};
1778 } else {
1779 retVal = state;
1780 }
1781
1782 if (tableLocation.getRow() == -1) {
1783 clearSelectionAndLeadAnchor();
1784 } else {
1785 setRowSelectionInterval(tableLocation.getRow(),
1786 tableLocation.getRow());
1787 setColumnSelectionInterval(tableLocation.getColumn(),
1788 tableLocation.getColumn());
1789 }
1790 }
1791 }
1792
1793 DropLocation old = dropLocation;
1794 dropLocation = tableLocation;
1795 firePropertyChange("dropLocation", old, dropLocation);
1796
1797 return retVal;
1798 }
1799
1800 /**
1801 * Returns the location that this component should visually indicate
1802 * as the drop location during a DnD operation over the component,
1803 * or {@code null} if no location is to currently be shown.
1804 * <p>
1805 * This method is not meant for querying the drop location
1806 * from a {@code TransferHandler}, as the drop location is only
1807 * set after the {@code TransferHandler}'s <code>canImport</code>
1808 * has returned and has allowed for the location to be shown.
1809 * <p>
1810 * When this property changes, a property change event with
1811 * name "dropLocation" is fired by the component.
1812 *
1813 * @return the drop location
1814 * @see #setDropMode
1815 * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1816 * @since 1.6
1817 */
1818 public final DropLocation getDropLocation() {
1819 return dropLocation;
1820 }
1821
1822 /**
1823 * Specifies whether a {@code RowSorter} should be created for the
1824 * table whenever its model changes.
1825 * <p>
1826 * When {@code setAutoCreateRowSorter(true)} is invoked, a {@code
1827 * TableRowSorter} is immediately created and installed on the
1828 * table. While the {@code autoCreateRowSorter} property remains
1829 * {@code true}, every time the model is changed, a new {@code
1830 * TableRowSorter} is created and set as the table's row sorter.
1831 *
1832 * @param autoCreateRowSorter whether or not a {@code RowSorter}
1833 * should be automatically created
1834 * @see javax.swing.table.TableRowSorter
1835 * @beaninfo
1836 * bound: true
1837 * preferred: true
1838 * description: Whether or not to turn on sorting by default.
1839 * @since 1.6
1840 */
1841 public void setAutoCreateRowSorter(boolean autoCreateRowSorter) {
1842 boolean oldValue = this.autoCreateRowSorter;
1843 this.autoCreateRowSorter = autoCreateRowSorter;
1844 if (autoCreateRowSorter) {
1845 setRowSorter(new TableRowSorter<TableModel>(getModel()));
1846 }
1847 firePropertyChange("autoCreateRowSorter", oldValue,
1848 autoCreateRowSorter);
1849 }
1850
1851 /**
1852 * Returns {@code true} if whenever the model changes, a new
1853 * {@code RowSorter} should be created and installed
1854 * as the table's sorter; otherwise, returns {@code false}.
1855 *
1856 * @return true if a {@code RowSorter} should be created when
1857 * the model changes
1858 * @since 1.6
1859 */
1860 public boolean getAutoCreateRowSorter() {
1861 return autoCreateRowSorter;
1862 }
1863
1864 /**
1865 * Specifies whether the selection should be updated after sorting.
1866 * If true, on sorting the selection is reset such that
1867 * the same rows, in terms of the model, remain selected. The default
1868 * is true.
1869 *
1870 * @param update whether or not to update the selection on sorting
1871 * @beaninfo
1872 * bound: true
1873 * expert: true
1874 * description: Whether or not to update the selection on sorting
1875 * @since 1.6
1876 */
1877 public void setUpdateSelectionOnSort(boolean update) {
1878 if (updateSelectionOnSort != update) {
1879 updateSelectionOnSort = update;
1880 firePropertyChange("updateSelectionOnSort", !update, update);
1881 }
1882 }
1883
1884 /**
1885 * Returns true if the selection should be updated after sorting.
1886 *
1887 * @return whether to update the selection on a sort
1888 * @since 1.6
1889 */
1890 public boolean getUpdateSelectionOnSort() {
1891 return updateSelectionOnSort;
1892 }
1893
1894 /**
1895 * Sets the <code>RowSorter</code>. <code>RowSorter</code> is used
1896 * to provide sorting and filtering to a <code>JTable</code>.
1897 * <p>
1898 * This method clears the selection and resets any variable row heights.
1899 * <p>
1900 * This method fires a <code>PropertyChangeEvent</code> when appropriate,
1901 * with the property name <code>"rowSorter"</code>. For
1902 * backward-compatibility, this method fires an additional event with the
1903 * property name <code>"sorter"</code>.
1904 * <p>
1905 * If the underlying model of the <code>RowSorter</code> differs from
1906 * that of this <code>JTable</code> undefined behavior will result.
1907 *
1908 * @param sorter the <code>RowSorter</code>; <code>null</code> turns
1909 * sorting off
1910 * @see javax.swing.table.TableRowSorter
1911 * @beaninfo
1912 * bound: true
1913 * description: The table's RowSorter
1914 * @since 1.6
1915 */
1916 public void setRowSorter(RowSorter<? extends TableModel> sorter) {
1917 RowSorter<? extends TableModel> oldRowSorter = null;
1918 if (sortManager != null) {
1919 oldRowSorter = sortManager.sorter;
1920 sortManager.dispose();
1921 sortManager = null;
1922 }
1923 rowModel = null;
1924 clearSelectionAndLeadAnchor();
1925 if (sorter != null) {
1926 sortManager = new SortManager(sorter);
1927 }
1928 resizeAndRepaint();
1929 firePropertyChange("rowSorter", oldRowSorter, sorter);
1930 firePropertyChange("sorter", oldRowSorter, sorter);
1931 }
1932
1933 /**
1934 * Returns the object responsible for sorting.
1935 *
1936 * @return the object responsible for sorting
1937 * @since 1.6
1938 */
1939 public RowSorter<? extends TableModel> getRowSorter() {
1940 return (sortManager != null) ? sortManager.sorter : null;
1941 }
1942
1943 //
1944 // Selection methods
1945 //
1946 /**
1947 * Sets the table's selection mode to allow only single selections, a single
1948 * contiguous interval, or multiple intervals.
1949 * <P>
1950 * <bold>Note:</bold>
1951 * <code>JTable</code> provides all the methods for handling
1952 * column and row selection. When setting states,
1953 * such as <code>setSelectionMode</code>, it not only
1954 * updates the mode for the row selection model but also sets similar
1955 * values in the selection model of the <code>columnModel</code>.
1956 * If you want to have the row and column selection models operating
1957 * in different modes, set them both directly.
1958 * <p>
1959 * Both the row and column selection models for <code>JTable</code>
1960 * default to using a <code>DefaultListSelectionModel</code>
1961 * so that <code>JTable</code> works the same way as the
1962 * <code>JList</code>. See the <code>setSelectionMode</code> method
1963 * in <code>JList</code> for details about the modes.
1964 *
1965 * @see JList#setSelectionMode
1966 * @beaninfo
1967 * description: The selection mode used by the row and column selection models.
1968 * enum: SINGLE_SELECTION ListSelectionModel.SINGLE_SELECTION
1969 * SINGLE_INTERVAL_SELECTION ListSelectionModel.SINGLE_INTERVAL_SELECTION
1970 * MULTIPLE_INTERVAL_SELECTION ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
1971 */
1972 public void setSelectionMode(int selectionMode) {
1973 clearSelection();
1974 getSelectionModel().setSelectionMode(selectionMode);
1975 getColumnModel().getSelectionModel().setSelectionMode(selectionMode);
1976 }
1977
1978 /**
1979 * Sets whether the rows in this model can be selected.
1980 *
1981 * @param rowSelectionAllowed true if this model will allow row selection
1982 * @see #getRowSelectionAllowed
1983 * @beaninfo
1984 * bound: true
1985 * attribute: visualUpdate true
1986 * description: If true, an entire row is selected for each selected cell.
1987 */
1988 public void setRowSelectionAllowed(boolean rowSelectionAllowed) {
1989 boolean old = this.rowSelectionAllowed;
1990 this.rowSelectionAllowed = rowSelectionAllowed;
1991 if (old != rowSelectionAllowed) {
1992 repaint();
1993 }
1994 firePropertyChange("rowSelectionAllowed", old, rowSelectionAllowed);
1995 }
1996
1997 /**
1998 * Returns true if rows can be selected.
1999 *
2000 * @return true if rows can be selected, otherwise false
2001 * @see #setRowSelectionAllowed
2002 */
2003 public boolean getRowSelectionAllowed() {
2004 return rowSelectionAllowed;
2005 }
2006
2007 /**
2008 * Sets whether the columns in this model can be selected.
2009 *
2010 * @param columnSelectionAllowed true if this model will allow column selection
2011 * @see #getColumnSelectionAllowed
2012 * @beaninfo
2013 * bound: true
2014 * attribute: visualUpdate true
2015 * description: If true, an entire column is selected for each selected cell.
2016 */
2017 public void setColumnSelectionAllowed(boolean columnSelectionAllowed) {
2018 boolean old = columnModel.getColumnSelectionAllowed();
2019 columnModel.setColumnSelectionAllowed(columnSelectionAllowed);
2020 if (old != columnSelectionAllowed) {
2021 repaint();
2022 }
2023 firePropertyChange("columnSelectionAllowed", old, columnSelectionAllowed);
2024 }
2025
2026 /**
2027 * Returns true if columns can be selected.
2028 *
2029 * @return true if columns can be selected, otherwise false
2030 * @see #setColumnSelectionAllowed
2031 */
2032 public boolean getColumnSelectionAllowed() {
2033 return columnModel.getColumnSelectionAllowed();
2034 }
2035
2036 /**
2037 * Sets whether this table allows both a column selection and a
2038 * row selection to exist simultaneously. When set,
2039 * the table treats the intersection of the row and column selection
2040 * models as the selected cells. Override <code>isCellSelected</code> to
2041 * change this default behavior. This method is equivalent to setting
2042 * both the <code>rowSelectionAllowed</code> property and
2043 * <code>columnSelectionAllowed</code> property of the
2044 * <code>columnModel</code> to the supplied value.
2045 *
2046 * @param cellSelectionEnabled true if simultaneous row and column
2047 * selection is allowed
2048 * @see #getCellSelectionEnabled
2049 * @see #isCellSelected
2050 * @beaninfo
2051 * bound: true
2052 * attribute: visualUpdate true
2053 * description: Select a rectangular region of cells rather than
2054 * rows or columns.
2055 */
2056 public void setCellSelectionEnabled(boolean cellSelectionEnabled) {
2057 setRowSelectionAllowed(cellSelectionEnabled);
2058 setColumnSelectionAllowed(cellSelectionEnabled);
2059 boolean old = this.cellSelectionEnabled;
2060 this.cellSelectionEnabled = cellSelectionEnabled;
2061 firePropertyChange("cellSelectionEnabled", old, cellSelectionEnabled);
2062 }
2063
2064 /**
2065 * Returns true if both row and column selection models are enabled.
2066 * Equivalent to <code>getRowSelectionAllowed() &&
2067 * getColumnSelectionAllowed()</code>.
2068 *
2069 * @return true if both row and column selection models are enabled
2070 *
2071 * @see #setCellSelectionEnabled
2072 */
2073 public boolean getCellSelectionEnabled() {
2074 return getRowSelectionAllowed() && getColumnSelectionAllowed();
2075 }
2076
2077 /**
2078 * Selects all rows, columns, and cells in the table.
2079 */
2080 public void selectAll() {
2081 // If I'm currently editing, then I should stop editing
2082 if (isEditing()) {
2083 removeEditor();
2084 }
2085 if (getRowCount() > 0 && getColumnCount() > 0) {
2086 int oldLead;
2087 int oldAnchor;
2088 ListSelectionModel selModel;
2089
2090 selModel = selectionModel;
2091 selModel.setValueIsAdjusting(true);
2092 oldLead = getAdjustedIndex(selModel.getLeadSelectionIndex(), true);
2093 oldAnchor = getAdjustedIndex(selModel.getAnchorSelectionIndex(), true);
2094
2095 setRowSelectionInterval(0, getRowCount()-1);
2096
2097 // this is done to restore the anchor and lead
2098 SwingUtilities2.setLeadAnchorWithoutSelection(selModel, oldLead, oldAnchor);
2099
2100 selModel.setValueIsAdjusting(false);
2101
2102 selModel = columnModel.getSelectionModel();
2103 selModel.setValueIsAdjusting(true);
2104 oldLead = getAdjustedIndex(selModel.getLeadSelectionIndex(), false);
2105 oldAnchor = getAdjustedIndex(selModel.getAnchorSelectionIndex(), false);
2106
2107 setColumnSelectionInterval(0, getColumnCount()-1);
2108
2109 // this is done to restore the anchor and lead
2110 SwingUtilities2.setLeadAnchorWithoutSelection(selModel, oldLead, oldAnchor);
2111
2112 selModel.setValueIsAdjusting(false);
2113 }
2114 }
2115
2116 /**
2117 * Deselects all selected columns and rows.
2118 */
2119 public void clearSelection() {
2120 selectionModel.clearSelection();
2121 columnModel.getSelectionModel().clearSelection();
2122 }
2123
2124 private void clearSelectionAndLeadAnchor() {
2125 selectionModel.setValueIsAdjusting(true);
2126 columnModel.getSelectionModel().setValueIsAdjusting(true);
2127
2128 clearSelection();
2129
2130 selectionModel.setAnchorSelectionIndex(-1);
2131 selectionModel.setLeadSelectionIndex(-1);
2132 columnModel.getSelectionModel().setAnchorSelectionIndex(-1);
2133 columnModel.getSelectionModel().setLeadSelectionIndex(-1);
2134
2135 selectionModel.setValueIsAdjusting(false);
2136 columnModel.getSelectionModel().setValueIsAdjusting(false);
2137 }
2138
2139 private int getAdjustedIndex(int index, boolean row) {
2140 int compare = row ? getRowCount() : getColumnCount();
2141 return index < compare ? index : -1;
2142 }
2143
2144 private int boundRow(int row) throws IllegalArgumentException {
2145 if (row < 0 || row >= getRowCount()) {
2146 throw new IllegalArgumentException("Row index out of range");
2147 }
2148 return row;
2149 }
2150
2151 private int boundColumn(int col) {
2152 if (col< 0 || col >= getColumnCount()) {
2153 throw new IllegalArgumentException("Column index out of range");
2154 }
2155 return col;
2156 }
2157
2158 /**
2159 * Selects the rows from <code>index0</code> to <code>index1</code>,
2160 * inclusive.
2161 *
2162 * @exception IllegalArgumentException if <code>index0</code> or
2163 * <code>index1</code> lie outside
2164 * [0, <code>getRowCount()</code>-1]
2165 * @param index0 one end of the interval
2166 * @param index1 the other end of the interval
2167 */
2168 public void setRowSelectionInterval(int index0, int index1) {
2169 selectionModel.setSelectionInterval(boundRow(index0), boundRow(index1));
2170 }
2171
2172 /**
2173 * Selects the columns from <code>index0</code> to <code>index1</code>,
2174 * inclusive.
2175 *
2176 * @exception IllegalArgumentException if <code>index0</code> or
2177 * <code>index1</code> lie outside
2178 * [0, <code>getColumnCount()</code>-1]
2179 * @param index0 one end of the interval
2180 * @param index1 the other end of the interval
2181 */
2182 public void setColumnSelectionInterval(int index0, int index1) {
2183 columnModel.getSelectionModel().setSelectionInterval(boundColumn(index0), boundColumn(index1));
2184 }
2185
2186 /**
2187 * Adds the rows from <code>index0</code> to <code>index1</code>, inclusive, to
2188 * the current selection.
2189 *
2190 * @exception IllegalArgumentException if <code>index0</code> or <code>index1</code>
2191 * lie outside [0, <code>getRowCount()</code>-1]
2192 * @param index0 one end of the interval
2193 * @param index1 the other end of the interval
2194 */
2195 public void addRowSelectionInterval(int index0, int index1) {
2196 selectionModel.addSelectionInterval(boundRow(index0), boundRow(index1));
2197 }
2198
2199 /**
2200 * Adds the columns from <code>index0</code> to <code>index1</code>,
2201 * inclusive, to the current selection.
2202 *
2203 * @exception IllegalArgumentException if <code>index0</code> or
2204 * <code>index1</code> lie outside
2205 * [0, <code>getColumnCount()</code>-1]
2206 * @param index0 one end of the interval
2207 * @param index1 the other end of the interval
2208 */
2209 public void addColumnSelectionInterval(int index0, int index1) {
2210 columnModel.getSelectionModel().addSelectionInterval(boundColumn(index0), boundColumn(index1));
2211 }
2212
2213 /**
2214 * Deselects the rows from <code>index0</code> to <code>index1</code>, inclusive.
2215 *
2216 * @exception IllegalArgumentException if <code>index0</code> or
2217 * <code>index1</code> lie outside
2218 * [0, <code>getRowCount()</code>-1]
2219 * @param index0 one end of the interval
2220 * @param index1 the other end of the interval
2221 */
2222 public void removeRowSelectionInterval(int index0, int index1) {
2223 selectionModel.removeSelectionInterval(boundRow(index0), boundRow(index1));
2224 }
2225
2226 /**
2227 * Deselects the columns from <code>index0</code> to <code>index1</code>, inclusive.
2228 *
2229 * @exception IllegalArgumentException if <code>index0</code> or
2230 * <code>index1</code> lie outside
2231 * [0, <code>getColumnCount()</code>-1]
2232 * @param index0 one end of the interval
2233 * @param index1 the other end of the interval
2234 */
2235 public void removeColumnSelectionInterval(int index0, int index1) {
2236 columnModel.getSelectionModel().removeSelectionInterval(boundColumn(index0), boundColumn(index1));
2237 }
2238
2239 /**
2240 * Returns the index of the first selected row, -1 if no row is selected.
2241 * @return the index of the first selected row
2242 */
2243 public int getSelectedRow() {
2244 return selectionModel.getMinSelectionIndex();
2245 }
2246
2247 /**
2248 * Returns the index of the first selected column,
2249 * -1 if no column is selected.
2250 * @return the index of the first selected column
2251 */
2252 public int getSelectedColumn() {
2253 return columnModel.getSelectionModel().getMinSelectionIndex();
2254 }
2255
2256 /**
2257 * Returns the indices of all selected rows.
2258 *
2259 * @return an array of integers containing the indices of all selected rows,
2260 * or an empty array if no row is selected
2261 * @see #getSelectedRow
2262 */
2263 public int[] getSelectedRows() {
2264 int iMin = selectionModel.getMinSelectionIndex();
2265 int iMax = selectionModel.getMaxSelectionIndex();
2266
2267 if ((iMin == -1) || (iMax == -1)) {
2268 return new int[0];
2269 }
2270
2271 int[] rvTmp = new int[1+ (iMax - iMin)];
2272 int n = 0;
2273 for(int i = iMin; i <= iMax; i++) {
2274 if (selectionModel.isSelectedIndex(i)) {
2275 rvTmp[n++] = i;
2276 }
2277 }
2278 int[] rv = new int[n];
2279 System.arraycopy(rvTmp, 0, rv, 0, n);
2280 return rv;
2281 }
2282
2283 /**
2284 * Returns the indices of all selected columns.
2285 *
2286 * @return an array of integers containing the indices of all selected columns,
2287 * or an empty array if no column is selected
2288 * @see #getSelectedColumn
2289 */
2290 public int[] getSelectedColumns() {
2291 return columnModel.getSelectedColumns();
2292 }
2293
2294 /**
2295 * Returns the number of selected rows.
2296 *
2297 * @return the number of selected rows, 0 if no rows are selected
2298 */
2299 public int getSelectedRowCount() {
2300 int iMin = selectionModel.getMinSelectionIndex();
2301 int iMax = selectionModel.getMaxSelectionIndex();
2302 int count = 0;
2303
2304 for(int i = iMin; i <= iMax; i++) {
2305 if (selectionModel.isSelectedIndex(i)) {
2306 count++;
2307 }
2308 }
2309 return count;
2310 }
2311
2312 /**
2313 * Returns the number of selected columns.
2314 *
2315 * @return the number of selected columns, 0 if no columns are selected
2316 */
2317 public int getSelectedColumnCount() {
2318 return columnModel.getSelectedColumnCount();
2319 }
2320
2321 /**
2322 * Returns true if the specified index is in the valid range of rows,
2323 * and the row at that index is selected.
2324 *
2325 * @return true if <code>row</code> is a valid index and the row at
2326 * that index is selected (where 0 is the first row)
2327 */
2328 public boolean isRowSelected(int row) {
2329 return selectionModel.isSelectedIndex(row);
2330 }
2331
2332 /**
2333 * Returns true if the specified index is in the valid range of columns,
2334 * and the column at that index is selected.
2335 *
2336 * @param column the column in the column model
2337 * @return true if <code>column</code> is a valid index and the column at
2338 * that index is selected (where 0 is the first column)
2339 */
2340 public boolean isColumnSelected(int column) {
2341 return columnModel.getSelectionModel().isSelectedIndex(column);
2342 }
2343
2344 /**
2345 * Returns true if the specified indices are in the valid range of rows
2346 * and columns and the cell at the specified position is selected.
2347 * @param row the row being queried
2348 * @param column the column being queried
2349 *
2350 * @return true if <code>row</code> and <code>column</code> are valid indices
2351 * and the cell at index <code>(row, column)</code> is selected,
2352 * where the first row and first column are at index 0
2353 */
2354 public boolean isCellSelected(int row, int column) {
2355 if (!getRowSelectionAllowed() && !getColumnSelectionAllowed()) {
2356 return false;
2357 }
2358 return (!getRowSelectionAllowed() || isRowSelected(row)) &&
2359 (!getColumnSelectionAllowed() || isColumnSelected(column));
2360 }
2361
2362 private void changeSelectionModel(ListSelectionModel sm, int index,
2363 boolean toggle, boolean extend, boolean selected,
2364 int anchor, boolean anchorSelected) {
2365 if (extend) {
2366 if (toggle) {
2367 if (anchorSelected) {
2368 sm.addSelectionInterval(anchor, index);
2369 } else {
2370 sm.removeSelectionInterval(anchor, index);
2371 // this is a Windows-only behavior that we want for file lists
2372 if (Boolean.TRUE == getClientProperty("Table.isFileList")) {
2373 sm.addSelectionInterval(index, index);
2374 sm.setAnchorSelectionIndex(anchor);
2375 }
2376 }
2377 }
2378 else {
2379 sm.setSelectionInterval(anchor, index);
2380 }
2381 }
2382 else {
2383 if (toggle) {
2384 if (selected) {
2385 sm.removeSelectionInterval(index, index);
2386 }
2387 else {
2388 sm.addSelectionInterval(index, index);
2389 }
2390 }
2391 else {
2392 sm.setSelectionInterval(index, index);
2393 }
2394 }
2395 }
2396
2397 /**
2398 * Updates the selection models of the table, depending on the state of the
2399 * two flags: <code>toggle</code> and <code>extend</code>. Most changes
2400 * to the selection that are the result of keyboard or mouse events received
2401 * by the UI are channeled through this method so that the behavior may be
2402 * overridden by a subclass. Some UIs may need more functionality than
2403 * this method provides, such as when manipulating the lead for discontiguous
2404 * selection, and may not call into this method for some selection changes.
2405 * <p>
2406 * This implementation uses the following conventions:
2407 * <ul>
2408 * <li> <code>toggle</code>: <em>false</em>, <code>extend</code>: <em>false</em>.
2409 * Clear the previous selection and ensure the new cell is selected.
2410 * <li> <code>toggle</code>: <em>false</em>, <code>extend</code>: <em>true</em>.
2411 * Extend the previous selection from the anchor to the specified cell,
2412 * clearing all other selections.
2413 * <li> <code>toggle</code>: <em>true</em>, <code>extend</code>: <em>false</em>.
2414 * If the specified cell is selected, deselect it. If it is not selected, select it.
2415 * <li> <code>toggle</code>: <em>true</em>, <code>extend</code>: <em>true</em>.
2416 * Apply the selection state of the anchor to all cells between it and the
2417 * specified cell.
2418 * </ul>
2419 * @param rowIndex affects the selection at <code>row</code>
2420 * @param columnIndex affects the selection at <code>column</code>
2421 * @param toggle see description above
2422 * @param extend if true, extend the current selection
2423 *
2424 * @since 1.3
2425 */
2426 public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
2427 ListSelectionModel rsm = getSelectionModel();
2428 ListSelectionModel csm = getColumnModel().getSelectionModel();
2429
2430 int anchorRow = getAdjustedIndex(rsm.getAnchorSelectionIndex(), true);
2431 int anchorCol = getAdjustedIndex(csm.getAnchorSelectionIndex(), false);
2432
2433 boolean anchorSelected = true;
2434
2435 if (anchorRow == -1) {
2436 if (getRowCount() > 0) {
2437 anchorRow = 0;
2438 }
2439 anchorSelected = false;
2440 }
2441
2442 if (anchorCol == -1) {
2443 if (getColumnCount() > 0) {
2444 anchorCol = 0;
2445 }
2446 anchorSelected = false;
2447 }
2448
2449 // Check the selection here rather than in each selection model.
2450 // This is significant in cell selection mode if we are supposed
2451 // to be toggling the selection. In this case it is better to
2452 // ensure that the cell's selection state will indeed be changed.
2453 // If this were done in the code for the selection model it
2454 // might leave a cell in selection state if the row was
2455 // selected but the column was not - as it would toggle them both.
2456 boolean selected = isCellSelected(rowIndex, columnIndex);
2457 anchorSelected = anchorSelected && isCellSelected(anchorRow, anchorCol);
2458
2459 changeSelectionModel(csm, columnIndex, toggle, extend, selected,
2460 anchorCol, anchorSelected);
2461 changeSelectionModel(rsm, rowIndex, toggle, extend, selected,
2462 anchorRow, anchorSelected);
2463
2464 // Scroll after changing the selection as blit scrolling is immediate,
2465 // so that if we cause the repaint after the scroll we end up painting
2466 // everything!
2467 if (getAutoscrolls()) {
2468 Rectangle cellRect = getCellRect(rowIndex, columnIndex, false);
2469 if (cellRect != null) {
2470 scrollRectToVisible(cellRect);
2471 }
2472 }
2473 }
2474
2475 /**
2476 * Returns the foreground color for selected cells.
2477 *
2478 * @return the <code>Color</code> object for the foreground property
2479 * @see #setSelectionForeground
2480 * @see #setSelectionBackground
2481 */
2482 public Color getSelectionForeground() {
2483 return selectionForeground;
2484 }
2485
2486 /**
2487 * Sets the foreground color for selected cells. Cell renderers
2488 * can use this color to render text and graphics for selected
2489 * cells.
2490 * <p>
2491 * The default value of this property is defined by the look
2492 * and feel implementation.
2493 * <p>
2494 * This is a <a href="http://java.sun.com/docs/books/tutorial/javabeans/properties/bound.html">JavaBeans</a> bound property.
2495 *
2496 * @param selectionForeground the <code>Color</code> to use in the foreground
2497 * for selected list items
2498 * @see #getSelectionForeground
2499 * @see #setSelectionBackground
2500 * @see #setForeground
2501 * @see #setBackground
2502 * @see #setFont
2503 * @beaninfo
2504 * bound: true
2505 * description: A default foreground color for selected cells.
2506 */
2507 public void setSelectionForeground(Color selectionForeground) {
2508 Color old = this.selectionForeground;
2509 this.selectionForeground = selectionForeground;
2510 firePropertyChange("selectionForeground", old, selectionForeground);
2511 repaint();
2512 }
2513
2514 /**
2515 * Returns the background color for selected cells.
2516 *
2517 * @return the <code>Color</code> used for the background of selected list items
2518 * @see #setSelectionBackground
2519 * @see #setSelectionForeground
2520 */
2521 public Color getSelectionBackground() {
2522 return selectionBackground;
2523 }
2524
2525 /**
2526 * Sets the background color for selected cells. Cell renderers
2527 * can use this color to the fill selected cells.
2528 * <p>
2529 * The default value of this property is defined by the look
2530 * and feel implementation.
2531 * <p>
2532 * This is a <a href="http://java.sun.com/docs/books/tutorial/javabeans/properties/bound.html">JavaBeans</a> bound property.
2533 *
2534 * @param selectionBackground the <code>Color</code> to use for the background
2535 * of selected cells
2536 * @see #getSelectionBackground
2537 * @see #setSelectionForeground
2538 * @see #setForeground
2539 * @see #setBackground
2540 * @see #setFont
2541 * @beaninfo
2542 * bound: true
2543 * description: A default background color for selected cells.
2544 */
2545 public void setSelectionBackground(Color selectionBackground) {
2546 Color old = this.selectionBackground;
2547 this.selectionBackground = selectionBackground;
2548 firePropertyChange("selectionBackground", old, selectionBackground);
2549 repaint();
2550 }
2551
2552 /**
2553 * Returns the <code>TableColumn</code> object for the column in the table
2554 * whose identifier is equal to <code>identifier</code>, when compared using
2555 * <code>equals</code>.
2556 *
2557 * @return the <code>TableColumn</code> object that matches the identifier
2558 * @exception IllegalArgumentException if <code>identifier</code> is <code>null</code> or no <code>TableColumn</code> has this identifier
2559 *
2560 * @param identifier the identifier object
2561 */
2562 public TableColumn getColumn(Object identifier) {
2563 TableColumnModel cm = getColumnModel();
2564 int columnIndex = cm.getColumnIndex(identifier);
2565 return cm.getColumn(columnIndex);
2566 }
2567
2568 //
2569 // Informally implement the TableModel interface.
2570 //
2571
2572 /**
2573 * Maps the index of the column in the view at
2574 * <code>viewColumnIndex</code> to the index of the column
2575 * in the table model. Returns the index of the corresponding
2576 * column in the model. If <code>viewColumnIndex</code>
2577 * is less than zero, returns <code>viewColumnIndex</code>.
2578 *
2579 * @param viewColumnIndex the index of the column in the view
2580 * @return the index of the corresponding column in the model
2581 *
2582 * @see #convertColumnIndexToView
2583 */
2584 public int convertColumnIndexToModel(int viewColumnIndex) {
2585 return SwingUtilities2.convertColumnIndexToModel(
2586 getColumnModel(), viewColumnIndex);
2587 }
2588
2589 /**
2590 * Maps the index of the column in the table model at
2591 * <code>modelColumnIndex</code> to the index of the column
2592 * in the view. Returns the index of the
2593 * corresponding column in the view; returns -1 if this column is not
2594 * being displayed. If <code>modelColumnIndex</code> is less than zero,
2595 * returns <code>modelColumnIndex</code>.
2596 *
2597 * @param modelColumnIndex the index of the column in the model
2598 * @return the index of the corresponding column in the view
2599 *
2600 * @see #convertColumnIndexToModel
2601 */
2602 public int convertColumnIndexToView(int modelColumnIndex) {
2603 return SwingUtilities2.convertColumnIndexToView(
2604 getColumnModel(), modelColumnIndex);
2605 }
2606
2607 /**
2608 * Maps the index of the row in terms of the
2609 * <code>TableModel</code> to the view. If the contents of the
2610 * model are not sorted the model and view indices are the same.
2611 *
2612 * @param modelRowIndex the index of the row in terms of the model
2613 * @return the index of the corresponding row in the view, or -1 if
2614 * the row isn't visible
2615 * @throws IndexOutOfBoundsException if sorting is enabled and passed an
2616 * index outside the number of rows of the <code>TableModel</code>
2617 * @see javax.swing.table.TableRowSorter
2618 * @since 1.6
2619 */
2620 public int convertRowIndexToView(int modelRowIndex) {
2621 RowSorter sorter = getRowSorter();
2622 if (sorter != null) {
2623 return sorter.convertRowIndexToView(modelRowIndex);
2624 }
2625 return modelRowIndex;
2626 }
2627
2628 /**
2629 * Maps the index of the row in terms of the view to the
2630 * underlying <code>TableModel</code>. If the contents of the
2631 * model are not sorted the model and view indices are the same.
2632 *
2633 * @param viewRowIndex the index of the row in the view
2634 * @return the index of the corresponding row in the model
2635 * @throws IndexOutOfBoundsException if sorting is enabled and passed an
2636 * index outside the range of the <code>JTable</code> as
2637 * determined by the method <code>getRowCount</code>
2638 * @see javax.swing.table.TableRowSorter
2639 * @see #getRowCount
2640 * @since 1.6
2641 */
2642 public int convertRowIndexToModel(int viewRowIndex) {
2643 RowSorter sorter = getRowSorter();
2644 if (sorter != null) {
2645 return sorter.convertRowIndexToModel(viewRowIndex);
2646 }
2647 return viewRowIndex;
2648 }
2649
2650 /**
2651 * Returns the number of rows that can be shown in the
2652 * <code>JTable</code>, given unlimited space. If a
2653 * <code>RowSorter</code> with a filter has been specified, the
2654 * number of rows returned may differ from that of the underlying
2655 * <code>TableModel</code>.
2656 *
2657 * @return the number of rows shown in the <code>JTable</code>
2658 * @see #getColumnCount
2659 */
2660 public int getRowCount() {
2661 RowSorter sorter = getRowSorter();
2662 if (sorter != null) {
2663 return sorter.getViewRowCount();
2664 }
2665 return getModel().getRowCount();
2666 }
2667
2668 /**
2669 * Returns the number of columns in the column model. Note that this may
2670 * be different from the number of columns in the table model.
2671 *
2672 * @return the number of columns in the table
2673 * @see #getRowCount
2674 * @see #removeColumn
2675 */
2676 public int getColumnCount() {
2677 return getColumnModel().getColumnCount();
2678 }
2679
2680 /**
2681 * Returns the name of the column appearing in the view at
2682 * column position <code>column</code>.
2683 *
2684 * @param column the column in the view being queried
2685 * @return the name of the column at position <code>column</code>
2686 in the view where the first column is column 0
2687 */
2688 public String getColumnName(int column) {
2689 return getModel().getColumnName(convertColumnIndexToModel(column));
2690 }
2691
2692 /**
2693 * Returns the type of the column appearing in the view at
2694 * column position <code>column</code>.
2695 *
2696 * @param column the column in the view being queried
2697 * @return the type of the column at position <code>column</code>
2698 * in the view where the first column is column 0
2699 */
2700 public Class<?> getColumnClass(int column) {
2701 return getModel().getColumnClass(convertColumnIndexToModel(column));
2702 }
2703
2704 /**
2705 * Returns the cell value at <code>row</code> and <code>column</code>.
2706 * <p>
2707 * <b>Note</b>: The column is specified in the table view's display
2708 * order, and not in the <code>TableModel</code>'s column
2709 * order. This is an important distinction because as the
2710 * user rearranges the columns in the table,
2711 * the column at a given index in the view will change.
2712 * Meanwhile the user's actions never affect the model's
2713 * column ordering.
2714 *
2715 * @param row the row whose value is to be queried
2716 * @param column the column whose value is to be queried
2717 * @return the Object at the specified cell
2718 */
2719 public Object getValueAt(int row, int column) {
2720 return getModel().getValueAt(convertRowIndexToModel(row),
2721 convertColumnIndexToModel(column));
2722 }
2723
2724 /**
2725 * Sets the value for the cell in the table model at <code>row</code>
2726 * and <code>column</code>.
2727 * <p>
2728 * <b>Note</b>: The column is specified in the table view's display
2729 * order, and not in the <code>TableModel</code>'s column
2730 * order. This is an important distinction because as the
2731 * user rearranges the columns in the table,
2732 * the column at a given index in the view will change.
2733 * Meanwhile the user's actions never affect the model's
2734 * column ordering.
2735 *
2736 * <code>aValue</code> is the new value.
2737 *
2738 * @param aValue the new value
2739 * @param row the row of the cell to be changed
2740 * @param column the column of the cell to be changed
2741 * @see #getValueAt
2742 */
2743 public void setValueAt(Object aValue, int row, int column) {
2744 getModel().setValueAt(aValue, convertRowIndexToModel(row),
2745 convertColumnIndexToModel(column));
2746 }
2747
2748 /**
2749 * Returns true if the cell at <code>row</code> and <code>column</code>
2750 * is editable. Otherwise, invoking <code>setValueAt</code> on the cell
2751 * will have no effect.
2752 * <p>
2753 * <b>Note</b>: The column is specified in the table view's display
2754 * order, and not in the <code>TableModel</code>'s column
2755 * order. This is an important distinction because as the
2756 * user rearranges the columns in the table,
2757 * the column at a given index in the view will change.
2758 * Meanwhile the user's actions never affect the model's
2759 * column ordering.
2760 *
2761 *
2762 * @param row the row whose value is to be queried
2763 * @param column the column whose value is to be queried
2764 * @return true if the cell is editable
2765 * @see #setValueAt
2766 */
2767 public boolean isCellEditable(int row, int column) {
2768 return getModel().isCellEditable(convertRowIndexToModel(row),
2769 convertColumnIndexToModel(column));
2770 }
2771 //
2772 // Adding and removing columns in the view
2773 //
2774
2775 /**
2776 * Appends <code>aColumn</code> to the end of the array of columns held by
2777 * this <code>JTable</code>'s column model.
2778 * If the column name of <code>aColumn</code> is <code>null</code>,
2779 * sets the column name of <code>aColumn</code> to the name
2780 * returned by <code>getModel().getColumnName()</code>.
2781 * <p>
2782 * To add a column to this <code>JTable</code> to display the
2783 * <code>modelColumn</code>'th column of data in the model with a
2784 * given <code>width</code>, <code>cellRenderer</code>,
2785 * and <code>cellEditor</code> you can use:
2786 * <pre>
2787 *
2788 * addColumn(new TableColumn(modelColumn, width, cellRenderer, cellEditor));
2789 *
2790 * </pre>
2791 * [Any of the <code>TableColumn</code> constructors can be used
2792 * instead of this one.]
2793 * The model column number is stored inside the <code>TableColumn</code>
2794 * and is used during rendering and editing to locate the appropriates
2795 * data values in the model. The model column number does not change
2796 * when columns are reordered in the view.
2797 *
2798 * @param aColumn the <code>TableColumn</code> to be added
2799 * @see #removeColumn
2800 */
2801 public void addColumn(TableColumn aColumn) {
2802 if (aColumn.getHeaderValue() == null) {
2803 int modelColumn = aColumn.getModelIndex();
2804 String columnName = getModel().getColumnName(modelColumn);
2805 aColumn.setHeaderValue(columnName);
2806 }
2807 getColumnModel().addColumn(aColumn);
2808 }
2809
2810 /**
2811 * Removes <code>aColumn</code> from this <code>JTable</code>'s
2812 * array of columns. Note: this method does not remove the column
2813 * of data from the model; it just removes the <code>TableColumn</code>
2814 * that was responsible for displaying it.
2815 *
2816 * @param aColumn the <code>TableColumn</code> to be removed
2817 * @see #addColumn
2818 */
2819 public void removeColumn(TableColumn aColumn) {
2820 getColumnModel().removeColumn(aColumn);
2821 }
2822
2823 /**
2824 * Moves the column <code>column</code> to the position currently
2825 * occupied by the column <code>targetColumn</code> in the view.
2826 * The old column at <code>targetColumn</code> is
2827 * shifted left or right to make room.
2828 *
2829 * @param column the index of column to be moved
2830 * @param targetColumn the new index of the column
2831 */
2832 public void moveColumn(int column, int targetColumn) {
2833 getColumnModel().moveColumn(column, targetColumn);
2834 }
2835
2836 //
2837 // Cover methods for various models and helper methods
2838 //
2839
2840 /**
2841 * Returns the index of the column that <code>point</code> lies in,
2842 * or -1 if the result is not in the range
2843 * [0, <code>getColumnCount()</code>-1].
2844 *
2845 * @param point the location of interest
2846 * @return the index of the column that <code>point</code> lies in,
2847 * or -1 if the result is not in the range
2848 * [0, <code>getColumnCount()</code>-1]
2849 * @see #rowAtPoint
2850 */
2851 public int columnAtPoint(Point point) {
2852 int x = point.x;
2853 if( !getComponentOrientation().isLeftToRight() ) {
2854 x = getWidth() - x - 1;
2855 }
2856 return getColumnModel().getColumnIndexAtX(x);
2857 }
2858
2859 /**
2860 * Returns the index of the row that <code>point</code> lies in,
2861 * or -1 if the result is not in the range
2862 * [0, <code>getRowCount()</code>-1].
2863 *
2864 * @param point the location of interest
2865 * @return the index of the row that <code>point</code> lies in,
2866 * or -1 if the result is not in the range
2867 * [0, <code>getRowCount()</code>-1]
2868 * @see #columnAtPoint
2869 */
2870 public int rowAtPoint(Point point) {
2871 int y = point.y;
2872 int result = (rowModel == null) ? y/getRowHeight() : rowModel.getIndex(y);
2873 if (result < 0) {
2874 return -1;
2875 }
2876 else if (result >= getRowCount()) {
2877 return -1;
2878 }
2879 else {
2880 return result;
2881 }
2882 }
2883
2884 /**
2885 * Returns a rectangle for the cell that lies at the intersection of
2886 * <code>row</code> and <code>column</code>.
2887 * If <code>includeSpacing</code> is true then the value returned
2888 * has the full height and width of the row and column
2889 * specified. If it is false, the returned rectangle is inset by the
2890 * intercell spacing to return the true bounds of the rendering or
2891 * editing component as it will be set during rendering.
2892 * <p>
2893 * If the column index is valid but the row index is less
2894 * than zero the method returns a rectangle with the
2895 * <code>y</code> and <code>height</code> values set appropriately
2896 * and the <code>x</code> and <code>width</code> values both set
2897 * to zero. In general, when either the row or column indices indicate a
2898 * cell outside the appropriate range, the method returns a rectangle
2899 * depicting the closest edge of the closest cell that is within
2900 * the table's range. When both row and column indices are out
2901 * of range the returned rectangle covers the closest
2902 * point of the closest cell.
2903 * <p>
2904 * In all cases, calculations that use this method to calculate
2905 * results along one axis will not fail because of anomalies in
2906 * calculations along the other axis. When the cell is not valid
2907 * the <code>includeSpacing</code> parameter is ignored.
2908 *
2909 * @param row the row index where the desired cell
2910 * is located
2911 * @param column the column index where the desired cell
2912 * is located in the display; this is not
2913 * necessarily the same as the column index
2914 * in the data model for the table; the
2915 * {@link #convertColumnIndexToView(int)}
2916 * method may be used to convert a data
2917 * model column index to a display
2918 * column index
2919 * @param includeSpacing if false, return the true cell bounds -
2920 * computed by subtracting the intercell
2921 * spacing from the height and widths of
2922 * the column and row models
2923 *
2924 * @return the rectangle containing the cell at location
2925 * <code>row</code>,<code>column</code>
2926 * @see #getIntercellSpacing
2927 */
2928 public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
2929 Rectangle r = new Rectangle();
2930 boolean valid = true;
2931 if (row < 0) {
2932 // y = height = 0;
2933 valid = false;
2934 }
2935 else if (row >= getRowCount()) {
2936 r.y = getHeight();
2937 valid = false;
2938 }
2939 else {
2940 r.height = getRowHeight(row);
2941 r.y = (rowModel == null) ? row * r.height : rowModel.getPosition(row);
2942 }
2943
2944 if (column < 0) {
2945 if( !getComponentOrientation().isLeftToRight() ) {
2946 r.x = getWidth();
2947 }
2948 // otherwise, x = width = 0;
2949 valid = false;
2950 }
2951 else if (column >= getColumnCount()) {
2952 if( getComponentOrientation().isLeftToRight() ) {
2953 r.x = getWidth();
2954 }
2955 // otherwise, x = width = 0;
2956 valid = false;
2957 }
2958 else {
2959 TableColumnModel cm = getColumnModel();
2960 if( getComponentOrientation().isLeftToRight() ) {
2961 for(int i = 0; i < column; i++) {
2962 r.x += cm.getColumn(i).getWidth();
2963 }
2964 } else {
2965 for(int i = cm.getColumnCount()-1; i > column; i--) {
2966 r.x += cm.getColumn(i).getWidth();
2967 }
2968 }
2969 r.width = cm.getColumn(column).getWidth();
2970 }
2971
2972 if (valid && !includeSpacing) {
2973 // Bound the margins by their associated dimensions to prevent
2974 // returning bounds with negative dimensions.
2975 int rm = Math.min(getRowMargin(), r.height);
2976 int cm = Math.min(getColumnModel().getColumnMargin(), r.width);
2977 // This is not the same as grow(), it rounds differently.
2978 r.setBounds(r.x + cm/2, r.y + rm/2, r.width - cm, r.height - rm);
2979 }
2980 return r;
2981 }
2982
2983 private int viewIndexForColumn(TableColumn aColumn) {
2984 TableColumnModel cm = getColumnModel();
2985 for (int column = 0; column < cm.getColumnCount(); column++) {
2986 if (cm.getColumn(column) == aColumn) {
2987 return column;
2988 }
2989 }
2990 return -1;
2991 }
2992
2993 /**
2994 * Causes this table to lay out its rows and columns. Overridden so
2995 * that columns can be resized to accomodate a change in the size of
2996 * a containing parent.
2997 * Resizes one or more of the columns in the table
2998 * so that the total width of all of this <code>JTable</code>'s
2999 * columns is equal to the width of the table.
3000 * <p>
3001 * Before the layout begins the method gets the
3002 * <code>resizingColumn</code> of the <code>tableHeader</code>.
3003 * When the method is called as a result of the resizing of an enclosing window,
3004 * the <code>resizingColumn</code> is <code>null</code>. This means that resizing
3005 * has taken place "outside" the <code>JTable</code> and the change -
3006 * or "delta" - should be distributed to all of the columns regardless
3007 * of this <code>JTable</code>'s automatic resize mode.
3008 * <p>
3009 * If the <code>resizingColumn</code> is not <code>null</code>, it is one of
3010 * the columns in the table that has changed size rather than
3011 * the table itself. In this case the auto-resize modes govern
3012 * the way the extra (or deficit) space is distributed
3013 * amongst the available columns.
3014 * <p>
3015 * The modes are:
3016 * <ul>
3017 * <li> AUTO_RESIZE_OFF: Don't automatically adjust the column's
3018 * widths at all. Use a horizontal scrollbar to accomodate the
3019 * columns when their sum exceeds the width of the
3020 * <code>Viewport</code>. If the <code>JTable</code> is not
3021 * enclosed in a <code>JScrollPane</code> this may
3022 * leave parts of the table invisible.
3023 * <li> AUTO_RESIZE_NEXT_COLUMN: Use just the column after the
3024 * resizing column. This results in the "boundary" or divider
3025 * between adjacent cells being independently adjustable.
3026 * <li> AUTO_RESIZE_SUBSEQUENT_COLUMNS: Use all columns after the
3027 * one being adjusted to absorb the changes. This is the
3028 * default behavior.
3029 * <li> AUTO_RESIZE_LAST_COLUMN: Automatically adjust the
3030 * size of the last column only. If the bounds of the last column
3031 * prevent the desired size from being allocated, set the
3032 * width of the last column to the appropriate limit and make
3033 * no further adjustments.
3034 * <li> AUTO_RESIZE_ALL_COLUMNS: Spread the delta amongst all the columns
3035 * in the <code>JTable</code>, including the one that is being
3036 * adjusted.
3037 * </ul>
3038 * <p>
3039 * <bold>Note:</bold> When a <code>JTable</code> makes adjustments
3040 * to the widths of the columns it respects their minimum and
3041 * maximum values absolutely. It is therefore possible that,
3042 * even after this method is called, the total width of the columns
3043 * is still not equal to the width of the table. When this happens
3044 * the <code>JTable</code> does not put itself
3045 * in AUTO_RESIZE_OFF mode to bring up a scroll bar, or break other
3046 * commitments of its current auto-resize mode -- instead it
3047 * allows its bounds to be set larger (or smaller) than the total of the
3048 * column minimum or maximum, meaning, either that there
3049 * will not be enough room to display all of the columns, or that the
3050 * columns will not fill the <code>JTable</code>'s bounds.
3051 * These respectively, result in the clipping of some columns
3052 * or an area being painted in the <code>JTable</code>'s
3053 * background color during painting.
3054 * <p>
3055 * The mechanism for distributing the delta amongst the available
3056 * columns is provided in a private method in the <code>JTable</code>
3057 * class:
3058 * <pre>
3059 * adjustSizes(long targetSize, final Resizable3 r, boolean inverse)
3060 * </pre>
3061 * an explanation of which is provided in the following section.
3062 * <code>Resizable3</code> is a private
3063 * interface that allows any data structure containing a collection
3064 * of elements with a size, preferred size, maximum size and minimum size
3065 * to have its elements manipulated by the algorithm.
3066 * <p>
3067 * <H3> Distributing the delta </H3>
3068 * <p>
3069 * <H4> Overview </H4>
3070 * <P>
3071 * Call "DELTA" the difference between the target size and the
3072 * sum of the preferred sizes of the elements in r. The individual
3073 * sizes are calculated by taking the original preferred
3074 * sizes and adding a share of the DELTA - that share being based on
3075 * how far each preferred size is from its limiting bound (minimum or
3076 * maximum).
3077 * <p>
3078 * <H4>Definition</H4>
3079 * <P>
3080 * Call the individual constraints min[i], max[i], and pref[i].
3081 * <p>
3082 * Call their respective sums: MIN, MAX, and PREF.
3083 * <p>
3084 * Each new size will be calculated using:
3085 * <p>
3086 * <pre>
3087 * size[i] = pref[i] + delta[i]
3088 * </pre>
3089 * where each individual delta[i] is calculated according to:
3090 * <p>
3091 * If (DELTA < 0) we are in shrink mode where:
3092 * <p>
3093 * <PRE>
3094 * DELTA
3095 * delta[i] = ------------ * (pref[i] - min[i])
3096 * (PREF - MIN)
3097 * </PRE>
3098 * If (DELTA > 0) we are in expand mode where:
3099 * <p>
3100 * <PRE>
3101 * DELTA
3102 * delta[i] = ------------ * (max[i] - pref[i])
3103 * (MAX - PREF)
3104 * </PRE>
3105 * <P>
3106 * The overall effect is that the total size moves that same percentage,
3107 * k, towards the total minimum or maximum and that percentage guarantees
3108 * accomodation of the required space, DELTA.
3109 *
3110 * <H4>Details</H4>
3111 * <P>
3112 * Naive evaluation of the formulae presented here would be subject to
3113 * the aggregated rounding errors caused by doing this operation in finite
3114 * precision (using ints). To deal with this, the multiplying factor above,
3115 * is constantly recalculated and this takes account of the rounding
3116 * errors in the previous iterations. The result is an algorithm that
3117 * produces a set of integers whose values exactly sum to the supplied
3118 * <code>targetSize</code>, and does so by spreading the rounding
3119 * errors evenly over the given elements.
3120 *
3121 * <H4>When the MAX and MIN bounds are hit</H4>
3122 * <P>
3123 * When <code>targetSize</code> is outside the [MIN, MAX] range,
3124 * the algorithm sets all sizes to their appropriate limiting value
3125 * (maximum or minimum).
3126 *
3127 */
3128 public void doLayout() {
3129 TableColumn resizingColumn = getResizingColumn();
3130 if (resizingColumn == null) {
3131 setWidthsFromPreferredWidths(false);
3132 }
3133 else {
3134 // JTable behaves like a layout manger - but one in which the
3135 // user can come along and dictate how big one of the children
3136 // (columns) is supposed to be.
3137
3138 // A column has been resized and JTable may need to distribute
3139 // any overall delta to other columns, according to the resize mode.
3140 int columnIndex = viewIndexForColumn(resizingColumn);
3141 int delta = getWidth() - getColumnModel().getTotalColumnWidth();
3142 accommodateDelta(columnIndex, delta);
3143 delta = getWidth() - getColumnModel().getTotalColumnWidth();
3144
3145 // If the delta cannot be completely accomodated, then the
3146 // resizing column will have to take any remainder. This means
3147 // that the column is not being allowed to take the requested
3148 // width. This happens under many circumstances: For example,
3149 // AUTO_RESIZE_NEXT_COLUMN specifies that any delta be distributed
3150 // to the column after the resizing column. If one were to attempt
3151 // to resize the last column of the table, there would be no
3152 // columns after it, and hence nowhere to distribute the delta.
3153 // It would then be given entirely back to the resizing column,
3154 // preventing it from changing size.
3155 if (delta != 0) {
3156 resizingColumn.setWidth(resizingColumn.getWidth() + delta);
3157 }
3158
3159 // At this point the JTable has to work out what preferred sizes
3160 // would have resulted in the layout the user has chosen.
3161 // Thereafter, during window resizing etc. it has to work off
3162 // the preferred sizes as usual - the idea being that, whatever
3163 // the user does, everything stays in synch and things don't jump
3164 // around.
3165 setWidthsFromPreferredWidths(true);
3166 }
3167
3168 super.doLayout();
3169 }
3170
3171 private TableColumn getResizingColumn() {
3172 return (tableHeader == null) ? null
3173 : tableHeader.getResizingColumn();
3174 }
3175
3176 /**
3177 * Sizes the table columns to fit the available space.
3178 * @deprecated As of Swing version 1.0.3,
3179 * replaced by <code>doLayout()</code>.
3180 * @see #doLayout
3181 */
3182 @Deprecated
3183 public void sizeColumnsToFit(boolean lastColumnOnly) {
3184 int oldAutoResizeMode = autoResizeMode;
3185 setAutoResizeMode(lastColumnOnly ? AUTO_RESIZE_LAST_COLUMN
3186 : AUTO_RESIZE_ALL_COLUMNS);
3187 sizeColumnsToFit(-1);
3188 setAutoResizeMode(oldAutoResizeMode);
3189 }
3190
3191 /**
3192 * Obsolete as of Java 2 platform v1.4. Please use the
3193 * <code>doLayout()</code> method instead.
3194 * @param resizingColumn the column whose resizing made this adjustment
3195 * necessary or -1 if there is no such column
3196 * @see #doLayout
3197 */
3198 public void sizeColumnsToFit(int resizingColumn) {
3199 if (resizingColumn == -1) {
3200 setWidthsFromPreferredWidths(false);
3201 }
3202 else {
3203 if (autoResizeMode == AUTO_RESIZE_OFF) {
3204 TableColumn aColumn = getColumnModel().getColumn(resizingColumn);
3205 aColumn.setPreferredWidth(aColumn.getWidth());
3206 }
3207 else {
3208 int delta = getWidth() - getColumnModel().getTotalColumnWidth();
3209 accommodateDelta(resizingColumn, delta);
3210 setWidthsFromPreferredWidths(true);
3211 }
3212 }
3213 }
3214
3215 private void setWidthsFromPreferredWidths(final boolean inverse) {
3216 int totalWidth = getWidth();
3217 int totalPreferred = getPreferredSize().width;
3218 int target = !inverse ? totalWidth : totalPreferred;
3219
3220 final TableColumnModel cm = columnModel;
3221 Resizable3 r = new Resizable3() {
3222 public int getElementCount() { return cm.getColumnCount(); }
3223 public int getLowerBoundAt(int i) { return cm.getColumn(i).getMinWidth(); }
3224 public int getUpperBoundAt(int i) { return cm.getColumn(i).getMaxWidth(); }
3225 public int getMidPointAt(int i) {
3226 if (!inverse) {
3227 return cm.getColumn(i).getPreferredWidth();
3228 }
3229 else {
3230 return cm.getColumn(i).getWidth();
3231 }
3232 }
3233 public void setSizeAt(int s, int i) {
3234 if (!inverse) {
3235 cm.getColumn(i).setWidth(s);
3236 }
3237 else {
3238 cm.getColumn(i).setPreferredWidth(s);
3239 }
3240 }
3241 };
3242
3243 adjustSizes(target, r, inverse);
3244 }
3245
3246
3247 // Distribute delta over columns, as indicated by the autoresize mode.
3248 private void accommodateDelta(int resizingColumnIndex, int delta) {
3249 int columnCount = getColumnCount();
3250 int from = resizingColumnIndex;
3251 int to;
3252
3253 // Use the mode to determine how to absorb the changes.
3254 switch(autoResizeMode) {
3255 case AUTO_RESIZE_NEXT_COLUMN:
3256 from = from + 1;
3257 to = Math.min(from + 1, columnCount); break;
3258 case AUTO_RESIZE_SUBSEQUENT_COLUMNS:
3259 from = from + 1;
3260 to = columnCount; break;
3261 case AUTO_RESIZE_LAST_COLUMN:
3262 from = columnCount - 1;
3263 to = from + 1; break;
3264 case AUTO_RESIZE_ALL_COLUMNS:
3265 from = 0;
3266 to = columnCount; break;
3267 default:
3268 return;
3269 }
3270
3271 final int start = from;
3272 final int end = to;
3273 final TableColumnModel cm = columnModel;
3274 Resizable3 r = new Resizable3() {
3275 public int getElementCount() { return end-start; }
3276 public int getLowerBoundAt(int i) { return cm.getColumn(i+start).getMinWidth(); }
3277 public int getUpperBoundAt(int i) { return cm.getColumn(i+start).getMaxWidth(); }
3278 public int getMidPointAt(int i) { return cm.getColumn(i+start).getWidth(); }
3279 public void setSizeAt(int s, int i) { cm.getColumn(i+start).setWidth(s); }
3280 };
3281
3282 int totalWidth = 0;
3283 for(int i = from; i < to; i++) {
3284 TableColumn aColumn = columnModel.getColumn(i);
3285 int input = aColumn.getWidth();
3286 totalWidth = totalWidth + input;
3287 }
3288
3289 adjustSizes(totalWidth + delta, r, false);
3290 }
3291
3292 private interface Resizable2 {
3293 public int getElementCount();
3294 public int getLowerBoundAt(int i);
3295 public int getUpperBoundAt(int i);
3296 public void setSizeAt(int newSize, int i);
3297 }
3298
3299 private interface Resizable3 extends Resizable2 {
3300 public int getMidPointAt(int i);
3301 }
3302
3303
3304 private void adjustSizes(long target, final Resizable3 r, boolean inverse) {
3305 int N = r.getElementCount();
3306 long totalPreferred = 0;
3307 for(int i = 0; i < N; i++) {
3308 totalPreferred += r.getMidPointAt(i);
3309 }
3310 Resizable2 s;
3311 if ((target < totalPreferred) == !inverse) {
3312 s = new Resizable2() {
3313 public int getElementCount() { return r.getElementCount(); }
3314 public int getLowerBoundAt(int i) { return r.getLowerBoundAt(i); }
3315 public int getUpperBoundAt(int i) { return r.getMidPointAt(i); }
3316 public void setSizeAt(int newSize, int i) { r.setSizeAt(newSize, i); }
3317
3318 };
3319 }
3320 else {
3321 s = new Resizable2() {
3322 public int getElementCount() { return r.getElementCount(); }
3323 public int getLowerBoundAt(int i) { return r.getMidPointAt(i); }
3324 public int getUpperBoundAt(int i) { return r.getUpperBoundAt(i); }
3325 public void setSizeAt(int newSize, int i) { r.setSizeAt(newSize, i); }
3326
3327 };
3328 }
3329 adjustSizes(target, s, !inverse);
3330 }
3331
3332 private void adjustSizes(long target, Resizable2 r, boolean limitToRange) {
3333 long totalLowerBound = 0;
3334 long totalUpperBound = 0;
3335 for(int i = 0; i < r.getElementCount(); i++) {
3336 totalLowerBound += r.getLowerBoundAt(i);
3337 totalUpperBound += r.getUpperBoundAt(i);
3338 }
3339
3340 if (limitToRange) {
3341 target = Math.min(Math.max(totalLowerBound, target), totalUpperBound);
3342 }
3343
3344 for(int i = 0; i < r.getElementCount(); i++) {
3345 int lowerBound = r.getLowerBoundAt(i);
3346 int upperBound = r.getUpperBoundAt(i);
3347 // Check for zero. This happens when the distribution of the delta
3348 // finishes early due to a series of "fixed" entries at the end.
3349 // In this case, lowerBound == upperBound, for all subsequent terms.
3350 int newSize;
3351 if (totalLowerBound == totalUpperBound) {
3352 newSize = lowerBound;
3353 }
3354 else {
3355 double f = (double)(target - totalLowerBound)/(totalUpperBound - totalLowerBound);
3356 newSize = (int)Math.round(lowerBound+f*(upperBound - lowerBound));
3357 // We'd need to round manually in an all integer version.
3358 // size[i] = (int)(((totalUpperBound - target) * lowerBound +
3359 // (target - totalLowerBound) * upperBound)/(totalUpperBound-totalLowerBound));
3360 }
3361 r.setSizeAt(newSize, i);
3362 target -= newSize;
3363 totalLowerBound -= lowerBound;
3364 totalUpperBound -= upperBound;
3365 }
3366 }
3367
3368 /**
3369 * Overrides <code>JComponent</code>'s <code>getToolTipText</code>
3370 * method in order to allow the renderer's tips to be used
3371 * if it has text set.
3372 * <p>
3373 * <bold>Note:</bold> For <code>JTable</code> to properly display
3374 * tooltips of its renderers
3375 * <code>JTable</code> must be a registered component with the
3376 * <code>ToolTipManager</code>.
3377 * This is done automatically in <code>initializeLocalVars</code>,
3378 * but if at a later point <code>JTable</code> is told
3379 * <code>setToolTipText(null)</code> it will unregister the table
3380 * component, and no tips from renderers will display anymore.
3381 *
3382 * @see JComponent#getToolTipText
3383 */
3384 public String getToolTipText(MouseEvent event) {
3385 String tip = null;
3386 Point p = event.getPoint();
3387
3388 // Locate the renderer under the event location
3389 int hitColumnIndex = columnAtPoint(p);
3390 int hitRowIndex = rowAtPoint(p);
3391
3392 if ((hitColumnIndex != -1) && (hitRowIndex != -1)) {
3393 TableCellRenderer renderer = getCellRenderer(hitRowIndex, hitColumnIndex);
3394 Component component = prepareRenderer(renderer, hitRowIndex, hitColumnIndex);
3395
3396 // Now have to see if the component is a JComponent before
3397 // getting the tip
3398 if (component instanceof JComponent) {
3399 // Convert the event to the renderer's coordinate system
3400 Rectangle cellRect = getCellRect(hitRowIndex, hitColumnIndex, false);
3401 p.translate(-cellRect.x, -cellRect.y);
3402 MouseEvent newEvent = new MouseEvent(component, event.getID(),
3403 event.getWhen(), event.getModifiers(),
3404 p.x, p.y,
3405 event.getXOnScreen(),
3406 event.getYOnScreen(),
3407 event.getClickCount(),
3408 event.isPopupTrigger(),
3409 MouseEvent.NOBUTTON);
3410
3411 tip = ((JComponent)component).getToolTipText(newEvent);
3412 }
3413 }
3414
3415 // No tip from the renderer get our own tip
3416 if (tip == null)
3417 tip = getToolTipText();
3418
3419 return tip;
3420 }
3421
3422 //
3423 // Editing Support
3424 //
3425
3426 /**
3427 * Sets whether editors in this JTable get the keyboard focus
3428 * when an editor is activated as a result of the JTable
3429 * forwarding keyboard events for a cell.
3430 * By default, this property is false, and the JTable
3431 * retains the focus unless the cell is clicked.
3432 *
3433 * @param surrendersFocusOnKeystroke true if the editor should get the focus
3434 * when keystrokes cause the editor to be
3435 * activated
3436 *
3437 *
3438 * @see #getSurrendersFocusOnKeystroke
3439 * @since 1.4
3440 */
3441 public void setSurrendersFocusOnKeystroke(boolean surrendersFocusOnKeystroke) {
3442 this.surrendersFocusOnKeystroke = surrendersFocusOnKeystroke;
3443 }
3444
3445 /**
3446 * Returns true if the editor should get the focus
3447 * when keystrokes cause the editor to be activated
3448 *
3449 * @return true if the editor should get the focus
3450 * when keystrokes cause the editor to be
3451 * activated
3452 *
3453 * @see #setSurrendersFocusOnKeystroke
3454 * @since 1.4
3455 */
3456 public boolean getSurrendersFocusOnKeystroke() {
3457 return surrendersFocusOnKeystroke;
3458 }
3459
3460 /**
3461 * Programmatically starts editing the cell at <code>row</code> and
3462 * <code>column</code>, if those indices are in the valid range, and
3463 * the cell at those indices is editable.
3464 * Note that this is a convenience method for
3465 * <code>editCellAt(int, int, null)</code>.
3466 *
3467 * @param row the row to be edited
3468 * @param column the column to be edited
3469 * @return false if for any reason the cell cannot be edited,
3470 * or if the indices are invalid
3471 */
3472 public boolean editCellAt(int row, int column) {
3473 return editCellAt(row, column, null);
3474 }
3475
3476 /**
3477 * Programmatically starts editing the cell at <code>row</code> and
3478 * <code>column</code>, if those indices are in the valid range, and
3479 * the cell at those indices is editable.
3480 * To prevent the <code>JTable</code> from
3481 * editing a particular table, column or cell value, return false from
3482 * the <code>isCellEditable</code> method in the <code>TableModel</code>
3483 * interface.
3484 *
3485 * @param row the row to be edited
3486 * @param column the column to be edited
3487 * @param e event to pass into <code>shouldSelectCell</code>;
3488 * note that as of Java 2 platform v1.2, the call to
3489 * <code>shouldSelectCell</code> is no longer made
3490 * @return false if for any reason the cell cannot be edited,
3491 * or if the indices are invalid
3492 */
3493 public boolean editCellAt(int row, int column, EventObject e){
3494 if (cellEditor != null && !cellEditor.stopCellEditing()) {
3495 return false;
3496 }
3497
3498 if (row < 0 || row >= getRowCount() ||
3499 column < 0 || column >= getColumnCount()) {
3500 return false;
3501 }
3502
3503 if (!isCellEditable(row, column))
3504 return false;
3505
3506 if (editorRemover == null) {
3507 KeyboardFocusManager fm =
3508 KeyboardFocusManager.getCurrentKeyboardFocusManager();
3509 editorRemover = new CellEditorRemover(fm);
3510 fm.addPropertyChangeListener("permanentFocusOwner", editorRemover);
3511 }
3512
3513 TableCellEditor editor = getCellEditor(row, column);
3514 if (editor != null && editor.isCellEditable(e)) {
3515 editorComp = prepareEditor(editor, row, column);
3516 if (editorComp == null) {
3517 removeEditor();
3518 return false;
3519 }
3520 editorComp.setBounds(getCellRect(row, column, false));
3521 add(editorComp);
3522 editorComp.validate();
3523 editorComp.repaint();
3524
3525 setCellEditor(editor);
3526 setEditingRow(row);
3527 setEditingColumn(column);
3528 editor.addCellEditorListener(this);
3529
3530 return true;
3531 }
3532 return false;
3533 }
3534
3535 /**
3536 * Returns true if a cell is being edited.
3537 *
3538 * @return true if the table is editing a cell
3539 * @see #editingColumn
3540 * @see #editingRow
3541 */
3542 public boolean isEditing() {
3543 return cellEditor != null;
3544 }
3545
3546 /**
3547 * Returns the component that is handling the editing session.
3548 * If nothing is being edited, returns null.
3549 *
3550 * @return Component handling editing session
3551 */
3552 public Component getEditorComponent() {
3553 return editorComp;
3554 }
3555
3556 /**
3557 * Returns the index of the column that contains the cell currently
3558 * being edited. If nothing is being edited, returns -1.
3559 *
3560 * @return the index of the column that contains the cell currently
3561 * being edited; returns -1 if nothing being edited
3562 * @see #editingRow
3563 */
3564 public int getEditingColumn() {
3565 return editingColumn;
3566 }
3567
3568 /**
3569 * Returns the index of the row that contains the cell currently
3570 * being edited. If nothing is being edited, returns -1.
3571 *
3572 * @return the index of the row that contains the cell currently
3573 * being edited; returns -1 if nothing being edited
3574 * @see #editingColumn
3575 */
3576 public int getEditingRow() {
3577 return editingRow;
3578 }
3579
3580 //
3581 // Managing TableUI
3582 //
3583
3584 /**
3585 * Returns the L&F object that renders this component.
3586 *
3587 * @return the <code>TableUI</code> object that renders this component
3588 */
3589 public TableUI getUI() {
3590 return (TableUI)ui;
3591 }
3592
3593 /**
3594 * Sets the L&F object that renders this component and repaints.
3595 *
3596 * @param ui the TableUI L&F object
3597 * @see UIDefaults#getUI
3598 * @beaninfo
3599 * bound: true
3600 * hidden: true
3601 * attribute: visualUpdate true
3602 * description: The UI object that implements the Component's LookAndFeel.
3603 */
3604 public void setUI(TableUI ui) {
3605 if (this.ui != ui) {
3606 super.setUI(ui);
3607 repaint();
3608 }
3609 }
3610
3611 /**
3612 * Notification from the <code>UIManager</code> that the L&F has changed.
3613 * Replaces the current UI object with the latest version from the
3614 * <code>UIManager</code>.
3615 *
3616 * @see JComponent#updateUI
3617 */
3618 public void updateUI() {
3619 // Update the UIs of the cell renderers, cell editors and header renderers.
3620 TableColumnModel cm = getColumnModel();
3621 for(int column = 0; column < cm.getColumnCount(); column++) {
3622 TableColumn aColumn = cm.getColumn(column);
3623 SwingUtilities.updateRendererOrEditorUI(aColumn.getCellRenderer());
3624 SwingUtilities.updateRendererOrEditorUI(aColumn.getCellEditor());
3625 SwingUtilities.updateRendererOrEditorUI(aColumn.getHeaderRenderer());
3626 }
3627
3628 // Update the UIs of all the default renderers.
3629 Enumeration defaultRenderers = defaultRenderersByColumnClass.elements();
3630 while (defaultRenderers.hasMoreElements()) {
3631 SwingUtilities.updateRendererOrEditorUI(defaultRenderers.nextElement());
3632 }
3633
3634 // Update the UIs of all the default editors.
3635 Enumeration defaultEditors = defaultEditorsByColumnClass.elements();
3636 while (defaultEditors.hasMoreElements()) {
3637 SwingUtilities.updateRendererOrEditorUI(defaultEditors.nextElement());
3638 }
3639
3640 // Update the UI of the table header
3641 if (tableHeader != null && tableHeader.getParent() == null) {
3642 tableHeader.updateUI();
3643 }
3644
3645 // Update UI applied to parent ScrollPane
3646 configureEnclosingScrollPaneUI();
3647
3648 setUI((TableUI)UIManager.getUI(this));
3649 }
3650
3651 /**
3652 * Returns the suffix used to construct the name of the L&F class used to
3653 * render this component.
3654 *
3655 * @return the string "TableUI"
3656 * @see JComponent#getUIClassID
3657 * @see UIDefaults#getUI
3658 */
3659 public String getUIClassID() {
3660 return uiClassID;
3661 }
3662
3663
3664 //
3665 // Managing models
3666 //
3667
3668 /**
3669 * Sets the data model for this table to <code>newModel</code> and registers
3670 * with it for listener notifications from the new data model.
3671 *
3672 * @param dataModel the new data source for this table
3673 * @exception IllegalArgumentException if <code>newModel</code> is <code>null</code>
3674 * @see #getModel
3675 * @beaninfo
3676 * bound: true
3677 * description: The model that is the source of the data for this view.
3678 */
3679 public void setModel(TableModel dataModel) {
3680 if (dataModel == null) {
3681 throw new IllegalArgumentException("Cannot set a null TableModel");
3682 }
3683 if (this.dataModel != dataModel) {
3684 TableModel old = this.dataModel;
3685 if (old != null) {
3686 old.removeTableModelListener(this);
3687 }
3688 this.dataModel = dataModel;
3689 dataModel.addTableModelListener(this);
3690
3691 tableChanged(new TableModelEvent(dataModel, TableModelEvent.HEADER_ROW));
3692
3693 firePropertyChange("model", old, dataModel);
3694
3695 if (getAutoCreateRowSorter()) {
3696 setRowSorter(new TableRowSorter<TableModel>(dataModel));
3697 }
3698 }
3699 }
3700
3701 /**
3702 * Returns the <code>TableModel</code> that provides the data displayed by this
3703 * <code>JTable</code>.
3704 *
3705 * @return the <code>TableModel</code> that provides the data displayed by this <code>JTable</code>
3706 * @see #setModel
3707 */
3708 public TableModel getModel() {
3709 return dataModel;
3710 }
3711
3712 /**
3713 * Sets the column model for this table to <code>newModel</code> and registers
3714 * for listener notifications from the new column model. Also sets
3715 * the column model of the <code>JTableHeader</code> to <code>columnModel</code>.
3716 *
3717 * @param columnModel the new data source for this table
3718 * @exception IllegalArgumentException if <code>columnModel</code> is <code>null</code>
3719 * @see #getColumnModel
3720 * @beaninfo
3721 * bound: true
3722 * description: The object governing the way columns appear in the view.
3723 */
3724 public void setColumnModel(TableColumnModel columnModel) {
3725 if (columnModel == null) {
3726 throw new IllegalArgumentException("Cannot set a null ColumnModel");
3727 }
3728 TableColumnModel old = this.columnModel;
3729 if (columnModel != old) {
3730 if (old != null) {
3731 old.removeColumnModelListener(this);
3732 }
3733 this.columnModel = columnModel;
3734 columnModel.addColumnModelListener(this);
3735
3736 // Set the column model of the header as well.
3737 if (tableHeader != null) {
3738 tableHeader.setColumnModel(columnModel);
3739 }
3740
3741 firePropertyChange("columnModel", old, columnModel);
3742 resizeAndRepaint();
3743 }
3744 }
3745
3746 /**
3747 * Returns the <code>TableColumnModel</code> that contains all column information
3748 * of this table.
3749 *
3750 * @return the object that provides the column state of the table
3751 * @see #setColumnModel
3752 */
3753 public TableColumnModel getColumnModel() {
3754 return columnModel;
3755 }
3756
3757 /**
3758 * Sets the row selection model for this table to <code>newModel</code>
3759 * and registers for listener notifications from the new selection model.
3760 *
3761 * @param newModel the new selection model
3762 * @exception IllegalArgumentException if <code>newModel</code> is <code>null</code>
3763 * @see #getSelectionModel
3764 * @beaninfo
3765 * bound: true
3766 * description: The selection model for rows.
3767 */
3768 public void setSelectionModel(ListSelectionModel newModel) {
3769 if (newModel == null) {
3770 throw new IllegalArgumentException("Cannot set a null SelectionModel");
3771 }
3772
3773 ListSelectionModel oldModel = selectionModel;
3774
3775 if (newModel != oldModel) {
3776 if (oldModel != null) {
3777 oldModel.removeListSelectionListener(this);
3778 }
3779
3780 selectionModel = newModel;
3781 newModel.addListSelectionListener(this);
3782
3783 firePropertyChange("selectionModel", oldModel, newModel);
3784 repaint();
3785 }
3786 }
3787
3788 /**
3789 * Returns the <code>ListSelectionModel</code> that is used to maintain row
3790 * selection state.
3791 *
3792 * @return the object that provides row selection state, <code>null</code>
3793 * if row selection is not allowed
3794 * @see #setSelectionModel
3795 */
3796 public ListSelectionModel getSelectionModel() {
3797 return selectionModel;
3798 }
3799
3800 //
3801 // RowSorterListener
3802 //
3803
3804 /**
3805 * <code>RowSorterListener</code> notification that the
3806 * <code>RowSorter</code> has changed in some way.
3807 *
3808 * @param e the <code>RowSorterEvent</code> describing the change
3809 * @throws NullPointerException if <code>e</code> is <code>null</code>
3810 * @since 1.6
3811 */
3812 public void sorterChanged(RowSorterEvent e) {
3813 if (e.getType() == RowSorterEvent.Type.SORT_ORDER_CHANGED) {
3814 JTableHeader header = getTableHeader();
3815 if (header != null) {
3816 header.repaint();
3817 }
3818 }
3819 else if (e.getType() == RowSorterEvent.Type.SORTED) {
3820 sorterChanged = true;
3821 if (!ignoreSortChange) {
3822 sortedTableChanged(e, null);
3823 }
3824 }
3825 }
3826
3827
3828 /**
3829 * SortManager provides support for managing the selection and variable
3830 * row heights when sorting is enabled. This information is encapsulated
3831 * into a class to avoid bulking up JTable.
3832 */
3833 private final class SortManager {
3834 RowSorter<? extends TableModel> sorter;
3835
3836 // Selection, in terms of the model. This is lazily created
3837 // as needed.
3838 private ListSelectionModel modelSelection;
3839 private int modelLeadIndex;
3840 // Set to true while in the process of changing the selection.
3841 // If this is true the selection change is ignored.
3842 private boolean syncingSelection;
3843 // Temporary cache of selection, in terms of model. This is only used
3844 // if we don't need the full weight of modelSelection.
3845 private int[] lastModelSelection;
3846
3847 // Heights of the rows in terms of the model.
3848 private SizeSequence modelRowSizes;
3849
3850
3851 SortManager(RowSorter<? extends TableModel> sorter) {
3852 this.sorter = sorter;
3853 sorter.addRowSorterListener(JTable.this);
3854 }
3855
3856 /**
3857 * Disposes any resources used by this SortManager.
3858 */
3859 public void dispose() {
3860 if (sorter != null) {
3861 sorter.removeRowSorterListener(JTable.this);
3862 }
3863 }
3864
3865 /**
3866 * Sets the height for a row at a specified index.
3867 */
3868 public void setViewRowHeight(int viewIndex, int rowHeight) {
3869 if (modelRowSizes == null) {
3870 modelRowSizes = new SizeSequence(getModel().getRowCount(),
3871 getRowHeight());
3872 }
3873 modelRowSizes.setSize(convertRowIndexToModel(viewIndex),rowHeight);
3874 }
3875
3876 /**
3877 * Invoked when the underlying model has completely changed.
3878 */
3879 public void allChanged() {
3880 modelLeadIndex = -1;
3881 modelSelection = null;
3882 modelRowSizes = null;
3883 }
3884
3885 /**
3886 * Invoked when the selection, on the view, has changed.
3887 */
3888 public void viewSelectionChanged(ListSelectionEvent e) {
3889 if (!syncingSelection && modelSelection != null) {
3890 modelSelection = null;
3891 }
3892 }
3893
3894 /**
3895 * Invoked when either the table model has changed, or the RowSorter
3896 * has changed. This is invoked prior to notifying the sorter of the
3897 * change.
3898 */
3899 public void prepareForChange(RowSorterEvent sortEvent,
3900 ModelChange change) {
3901 if (getUpdateSelectionOnSort()) {
3902 cacheSelection(sortEvent, change);
3903 }
3904 }
3905
3906 /**
3907 * Updates the internal cache of the selection based on the change.
3908 */
3909 private void cacheSelection(RowSorterEvent sortEvent,
3910 ModelChange change) {
3911 if (sortEvent != null) {
3912 // sort order changed. If modelSelection is null and filtering
3913 // is enabled we need to cache the selection in terms of the
3914 // underlying model, this will allow us to correctly restore
3915 // the selection even if rows are filtered out.
3916 if (modelSelection == null &&
3917 sorter.getViewRowCount() != getModel().getRowCount()) {
3918 modelSelection = new DefaultListSelectionModel();
3919 ListSelectionModel viewSelection = getSelectionModel();
3920 int min = viewSelection.getMinSelectionIndex();
3921 int max = viewSelection.getMaxSelectionIndex();
3922 int modelIndex;
3923 for (int viewIndex = min; viewIndex <= max; viewIndex++) {
3924 if (viewSelection.isSelectedIndex(viewIndex)) {
3925 modelIndex = convertRowIndexToModel(
3926 sortEvent, viewIndex);
3927 if (modelIndex != -1) {
3928 modelSelection.addSelectionInterval(
3929 modelIndex, modelIndex);
3930 }
3931 }
3932 }
3933 modelIndex = convertRowIndexToModel(sortEvent,
3934 viewSelection.getLeadSelectionIndex());
3935 SwingUtilities2.setLeadAnchorWithoutSelection(
3936 modelSelection, modelIndex, modelIndex);
3937 } else if (modelSelection == null) {
3938 // Sorting changed, haven't cached selection in terms
3939 // of model and no filtering. Temporarily cache selection.
3940 cacheModelSelection(sortEvent);
3941 }
3942 } else if (change.allRowsChanged) {
3943 // All the rows have changed, chuck any cached selection.
3944 modelSelection = null;
3945 } else if (modelSelection != null) {
3946 // Table changed, reflect changes in cached selection model.
3947 switch(change.type) {
3948 case TableModelEvent.DELETE:
3949 modelSelection.removeIndexInterval(change.startModelIndex,
3950 change.endModelIndex);
3951 break;
3952 case TableModelEvent.INSERT:
3953 modelSelection.insertIndexInterval(change.startModelIndex,
3954 change.endModelIndex,
3955 true);
3956 break;
3957 default:
3958 break;
3959 }
3960 } else {
3961 // table changed, but haven't cached rows, temporarily
3962 // cache them.
3963 cacheModelSelection(null);
3964 }
3965 }
3966
3967 private void cacheModelSelection(RowSorterEvent sortEvent) {
3968 lastModelSelection = convertSelectionToModel(sortEvent);
3969 modelLeadIndex = convertRowIndexToModel(sortEvent,
3970 selectionModel.getLeadSelectionIndex());
3971 }
3972
3973 /**
3974 * Inovked when either the table has changed or the sorter has changed
3975 * and after the sorter has been notified. If necessary this will
3976 * reapply the selection and variable row heights.
3977 */
3978 public void processChange(RowSorterEvent sortEvent,
3979 ModelChange change,
3980 boolean sorterChanged) {
3981 if (change != null) {
3982 if (change.allRowsChanged) {
3983 modelRowSizes = null;
3984 rowModel = null;
3985 } else if (modelRowSizes != null) {
3986 if (change.type == TableModelEvent.INSERT) {
3987 modelRowSizes.insertEntries(change.startModelIndex,
3988 change.endModelIndex -
3989 change.startModelIndex + 1,
3990 getRowHeight());
3991 } else if (change.type == TableModelEvent.DELETE) {
3992 modelRowSizes.removeEntries(change.startModelIndex,
3993 change.endModelIndex -
3994 change.startModelIndex +1 );
3995 }
3996 }
3997 }
3998 if (sorterChanged) {
3999 setViewRowHeightsFromModel();
4000 restoreSelection(change);
4001 }
4002 }
4003
4004 /**
4005 * Resets the variable row heights in terms of the view from
4006 * that of the variable row heights in terms of the model.
4007 */
4008 private void setViewRowHeightsFromModel() {
4009 if (modelRowSizes != null) {
4010 rowModel.setSizes(getRowCount(), getRowHeight());
4011 for (int viewIndex = getRowCount() - 1; viewIndex >= 0;
4012 viewIndex--) {
4013 int modelIndex = convertRowIndexToModel(viewIndex);
4014 rowModel.setSize(viewIndex,
4015 modelRowSizes.getSize(modelIndex));
4016 }
4017 }
4018 }
4019
4020 /**
4021 * Restores the selection from that in terms of the model.
4022 */
4023 private void restoreSelection(ModelChange change) {
4024 syncingSelection = true;
4025 if (lastModelSelection != null) {
4026 restoreSortingSelection(lastModelSelection,
4027 modelLeadIndex, change);
4028 lastModelSelection = null;
4029 } else if (modelSelection != null) {
4030 ListSelectionModel viewSelection = getSelectionModel();
4031 viewSelection.setValueIsAdjusting(true);
4032 viewSelection.clearSelection();
4033 int min = modelSelection.getMinSelectionIndex();
4034 int max = modelSelection.getMaxSelectionIndex();
4035 int viewIndex;
4036 for (int modelIndex = min; modelIndex <= max; modelIndex++) {
4037 if (modelSelection.isSelectedIndex(modelIndex)) {
4038 viewIndex = convertRowIndexToView(modelIndex);
4039 if (viewIndex != -1) {
4040 viewSelection.addSelectionInterval(viewIndex,
4041 viewIndex);
4042 }
4043 }
4044 }
4045 // Restore the lead
4046 int viewLeadIndex = modelSelection.getLeadSelectionIndex();
4047 if (viewLeadIndex != -1) {
4048 viewLeadIndex = convertRowIndexToView(viewLeadIndex);
4049 }
4050 SwingUtilities2.setLeadAnchorWithoutSelection(
4051 viewSelection, viewLeadIndex, viewLeadIndex);
4052 viewSelection.setValueIsAdjusting(false);
4053 }
4054 syncingSelection = false;
4055 }
4056 }
4057
4058
4059 /**
4060 * ModelChange is used when sorting to restore state, it corresponds
4061 * to data from a TableModelEvent. The values are precalculated as
4062 * they are used extensively.
4063 */
4064 private final class ModelChange {
4065 // Starting index of the change, in terms of the model
4066 int startModelIndex;
4067
4068 // Ending index of the change, in terms of the model
4069 int endModelIndex;
4070
4071 // Type of change
4072 int type;
4073
4074 // Number of rows in the model
4075 int modelRowCount;
4076
4077 // The event that triggered this.
4078 TableModelEvent event;
4079
4080 // Length of the change (end - start + 1)
4081 int length;
4082
4083 // True if the event indicates all the contents have changed
4084 boolean allRowsChanged;
4085
4086 ModelChange(TableModelEvent e) {
4087 startModelIndex = Math.max(0, e.getFirstRow());
4088 endModelIndex = e.getLastRow();
4089 modelRowCount = getModel().getRowCount();
4090 if (endModelIndex < 0) {
4091 endModelIndex = Math.max(0, modelRowCount - 1);
4092 }
4093 length = endModelIndex - startModelIndex + 1;
4094 type = e.getType();
4095 event = e;
4096 allRowsChanged = (e.getLastRow() == Integer.MAX_VALUE);
4097 }
4098 }
4099
4100 /**
4101 * Invoked when <code>sorterChanged</code> is invoked, or
4102 * when <code>tableChanged</code> is invoked and sorting is enabled.
4103 */
4104 private void sortedTableChanged(RowSorterEvent sortedEvent,
4105 TableModelEvent e) {
4106 int editingModelIndex = -1;
4107 ModelChange change = (e != null) ? new ModelChange(e) : null;
4108
4109 if ((change == null || !change.allRowsChanged) &&
4110 this.editingRow != -1) {
4111 editingModelIndex = convertRowIndexToModel(sortedEvent,
4112 this.editingRow);
4113 }
4114
4115 sortManager.prepareForChange(sortedEvent, change);
4116
4117 if (e != null) {
4118 if (change.type == TableModelEvent.UPDATE) {
4119 repaintSortedRows(change);
4120 }
4121 notifySorter(change);
4122 if (change.type != TableModelEvent.UPDATE) {
4123 // If the Sorter is unsorted we will not have received
4124 // notification, force treating insert/delete as a change.
4125 sorterChanged = true;
4126 }
4127 }
4128 else {
4129 sorterChanged = true;
4130 }
4131
4132 sortManager.processChange(sortedEvent, change, sorterChanged);
4133
4134 if (sorterChanged) {
4135 // Update the editing row
4136 if (this.editingRow != -1) {
4137 int newIndex = (editingModelIndex == -1) ? -1 :
4138 convertRowIndexToView(editingModelIndex,change);
4139 restoreSortingEditingRow(newIndex);
4140 }
4141
4142 // And handle the appropriate repainting.
4143 if (e == null || change.type != TableModelEvent.UPDATE) {
4144 resizeAndRepaint();
4145 }
4146 }
4147
4148 // Check if lead/anchor need to be reset.
4149 if (change != null && change.allRowsChanged) {
4150 clearSelectionAndLeadAnchor();
4151 resizeAndRepaint();
4152 }
4153 }
4154
4155 /**
4156 * Repaints the sort of sorted rows in response to a TableModelEvent.
4157 */
4158 private void repaintSortedRows(ModelChange change) {
4159 if (change.startModelIndex > change.endModelIndex ||
4160 change.startModelIndex + 10 < change.endModelIndex) {
4161 // Too much has changed, punt
4162 repaint();
4163 return;
4164 }
4165 int eventColumn = change.event.getColumn();
4166 int columnViewIndex = eventColumn;
4167 if (columnViewIndex == TableModelEvent.ALL_COLUMNS) {
4168 columnViewIndex = 0;
4169 }
4170 else {
4171 columnViewIndex = convertColumnIndexToView(columnViewIndex);
4172 if (columnViewIndex == -1) {
4173 return;
4174 }
4175 }
4176 int modelIndex = change.startModelIndex;
4177 while (modelIndex <= change.endModelIndex) {
4178 int viewIndex = convertRowIndexToView(modelIndex++);
4179 if (viewIndex != -1) {
4180 Rectangle dirty = getCellRect(viewIndex, columnViewIndex,
4181 false);
4182 int x = dirty.x;
4183 int w = dirty.width;
4184 if (eventColumn == TableModelEvent.ALL_COLUMNS) {
4185 x = 0;
4186 w = getWidth();
4187 }
4188 repaint(x, dirty.y, w, dirty.height);
4189 }
4190 }
4191 }
4192
4193 /**
4194 * Restores the selection after a model event/sort order changes.
4195 * All coordinates are in terms of the model.
4196 */
4197 private void restoreSortingSelection(int[] selection, int lead,
4198 ModelChange change) {
4199 // Convert the selection from model to view
4200 for (int i = selection.length - 1; i >= 0; i--) {
4201 selection[i] = convertRowIndexToView(selection[i], change);
4202 }
4203 lead = convertRowIndexToView(lead, change);
4204
4205 // Check for the common case of no change in selection for 1 row
4206 if (selection.length == 0 ||
4207 (selection.length == 1 && selection[0] == getSelectedRow())) {
4208 return;
4209 }
4210
4211 // And apply the new selection
4212 selectionModel.setValueIsAdjusting(true);
4213 selectionModel.clearSelection();
4214 for (int i = selection.length - 1; i >= 0; i--) {
4215 if (selection[i] != -1) {
4216 selectionModel.addSelectionInterval(selection[i],
4217 selection[i]);
4218 }
4219 }
4220 SwingUtilities2.setLeadAnchorWithoutSelection(
4221 selectionModel, lead, lead);
4222 selectionModel.setValueIsAdjusting(false);
4223 }
4224
4225 /**
4226 * Restores the editing row after a model event/sort order change.
4227 *
4228 * @param editingRow new index of the editingRow, in terms of the view
4229 */
4230 private void restoreSortingEditingRow(int editingRow) {
4231 if (editingRow == -1) {
4232 // Editing row no longer being shown, cancel editing
4233 TableCellEditor editor = getCellEditor();
4234 if (editor != null) {
4235 // First try and cancel
4236 editor.cancelCellEditing();
4237 if (getCellEditor() != null) {
4238 // CellEditor didn't cede control, forcefully
4239 // remove it
4240 removeEditor();
4241 }
4242 }
4243 }
4244 else {
4245 // Repositioning handled in BasicTableUI
4246 this.editingRow = editingRow;
4247 repaint();
4248 }
4249 }
4250
4251 /**
4252 * Notifies the sorter of a change in the underlying model.
4253 */
4254 private void notifySorter(ModelChange change) {
4255 try {
4256 ignoreSortChange = true;
4257 sorterChanged = false;
4258 switch(change.type) {
4259 case TableModelEvent.UPDATE:
4260 if (change.event.getLastRow() == Integer.MAX_VALUE) {
4261 sortManager.sorter.allRowsChanged();
4262 } else if (change.event.getColumn() ==
4263 TableModelEvent.ALL_COLUMNS) {
4264 sortManager.sorter.rowsUpdated(change.startModelIndex,
4265 change.endModelIndex);
4266 } else {
4267 sortManager.sorter.rowsUpdated(change.startModelIndex,
4268 change.endModelIndex,
4269 change.event.getColumn());
4270 }
4271 break;
4272 case TableModelEvent.INSERT:
4273 sortManager.sorter.rowsInserted(change.startModelIndex,
4274 change.endModelIndex);
4275 break;
4276 case TableModelEvent.DELETE:
4277 sortManager.sorter.rowsDeleted(change.startModelIndex,
4278 change.endModelIndex);
4279 break;
4280 }
4281 } finally {
4282 ignoreSortChange = false;
4283 }
4284 }
4285
4286 /**
4287 * Converts a model index to view index. This is called when the
4288 * sorter or model changes and sorting is enabled.
4289 *
4290 * @param change describes the TableModelEvent that initiated the change;
4291 * will be null if called as the result of a sort
4292 */
4293 private int convertRowIndexToView(int modelIndex, ModelChange change) {
4294 if (modelIndex < 0) {
4295 return -1;
4296 }
4297 if (change != null && modelIndex >= change.startModelIndex) {
4298 if (change.type == TableModelEvent.INSERT) {
4299 if (modelIndex + change.length >= change.modelRowCount) {
4300 return -1;
4301 }
4302 return sortManager.sorter.convertRowIndexToView(
4303 modelIndex + change.length);
4304 }
4305 else if (change.type == TableModelEvent.DELETE) {
4306 if (modelIndex <= change.endModelIndex) {
4307 // deleted
4308 return -1;
4309 }
4310 else {
4311 if (modelIndex - change.length >= change.modelRowCount) {
4312 return -1;
4313 }
4314 return sortManager.sorter.convertRowIndexToView(
4315 modelIndex - change.length);
4316 }
4317 }
4318 // else, updated
4319 }
4320 if (modelIndex >= getModel().getRowCount()) {
4321 return -1;
4322 }
4323 return sortManager.sorter.convertRowIndexToView(modelIndex);
4324 }
4325
4326 /**
4327 * Converts the selection to model coordinates. This is used when
4328 * the model changes or the sorter changes.
4329 */
4330 private int[] convertSelectionToModel(RowSorterEvent e) {
4331 int[] selection = getSelectedRows();
4332 for (int i = selection.length - 1; i >= 0; i--) {
4333 selection[i] = convertRowIndexToModel(e, selection[i]);
4334 }
4335 return selection;
4336 }
4337
4338 private int convertRowIndexToModel(RowSorterEvent e, int viewIndex) {
4339 if (e != null) {
4340 if (e.getPreviousRowCount() == 0) {
4341 return viewIndex;
4342 }
4343 // range checking handled by RowSorterEvent
4344 return e.convertPreviousRowIndexToModel(viewIndex);
4345 }
4346 // Make sure the viewIndex is valid
4347 if (viewIndex < 0 || viewIndex >= getRowCount()) {
4348 return -1;
4349 }
4350 return convertRowIndexToModel(viewIndex);
4351 }
4352
4353 //
4354 // Implementing TableModelListener interface
4355 //
4356
4357 /**
4358 * Invoked when this table's <code>TableModel</code> generates
4359 * a <code>TableModelEvent</code>.
4360 * The <code>TableModelEvent</code> should be constructed in the
4361 * coordinate system of the model; the appropriate mapping to the
4362 * view coordinate system is performed by this <code>JTable</code>
4363 * when it receives the event.
4364 * <p>
4365 * Application code will not use these methods explicitly, they
4366 * are used internally by <code>JTable</code>.
4367 * <p>
4368 * Note that as of 1.3, this method clears the selection, if any.
4369 */
4370 public void tableChanged(TableModelEvent e) {
4371 if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) {
4372 // The whole thing changed
4373 clearSelectionAndLeadAnchor();
4374
4375 rowModel = null;
4376
4377 if (sortManager != null) {
4378 try {
4379 ignoreSortChange = true;
4380 sortManager.sorter.modelStructureChanged();
4381 } finally {
4382 ignoreSortChange = false;
4383 }
4384 sortManager.allChanged();
4385 }
4386
4387 if (getAutoCreateColumnsFromModel()) {
4388 // This will effect invalidation of the JTable and JTableHeader.
4389 createDefaultColumnsFromModel();
4390 return;
4391 }
4392
4393 resizeAndRepaint();
4394 return;
4395 }
4396
4397 if (sortManager != null) {
4398 sortedTableChanged(null, e);
4399 return;
4400 }
4401
4402 // The totalRowHeight calculated below will be incorrect if
4403 // there are variable height rows. Repaint the visible region,
4404 // but don't return as a revalidate may be necessary as well.
4405 if (rowModel != null) {
4406 repaint();
4407 }
4408
4409 if (e.getType() == TableModelEvent.INSERT) {
4410 tableRowsInserted(e);
4411 return;
4412 }
4413
4414 if (e.getType() == TableModelEvent.DELETE) {
4415 tableRowsDeleted(e);
4416 return;
4417 }
4418
4419 int modelColumn = e.getColumn();
4420 int start = e.getFirstRow();
4421 int end = e.getLastRow();
4422
4423 Rectangle dirtyRegion;
4424 if (modelColumn == TableModelEvent.ALL_COLUMNS) {
4425 // 1 or more rows changed
4426 dirtyRegion = new Rectangle(0, start * getRowHeight(),
4427 getColumnModel().getTotalColumnWidth(), 0);
4428 }
4429 else {
4430 // A cell or column of cells has changed.
4431 // Unlike the rest of the methods in the JTable, the TableModelEvent
4432 // uses the coordinate system of the model instead of the view.
4433 // This is the only place in the JTable where this "reverse mapping"
4434 // is used.
4435 int column = convertColumnIndexToView(modelColumn);
4436 dirtyRegion = getCellRect(start, column, false);
4437 }
4438
4439 // Now adjust the height of the dirty region according to the value of "end".
4440 // Check for Integer.MAX_VALUE as this will cause an overflow.
4441 if (end != Integer.MAX_VALUE) {
4442 dirtyRegion.height = (end-start+1)*getRowHeight();
4443 repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height);
4444 }
4445 // In fact, if the end is Integer.MAX_VALUE we need to revalidate anyway
4446 // because the scrollbar may need repainting.
4447 else {
4448 clearSelectionAndLeadAnchor();
4449 resizeAndRepaint();
4450 rowModel = null;
4451 }
4452 }
4453
4454 /*
4455 * Invoked when rows have been inserted into the table.
4456 * <p>
4457 * Application code will not use these methods explicitly, they
4458 * are used internally by JTable.
4459 *
4460 * @param e the TableModelEvent encapsulating the insertion
4461 */
4462 private void tableRowsInserted(TableModelEvent e) {
4463 int start = e.getFirstRow();
4464 int end = e.getLastRow();
4465 if (start < 0) {
4466 start = 0;
4467 }
4468 if (end < 0) {
4469 end = getRowCount()-1;
4470 }
4471
4472 // Adjust the selection to account for the new rows.
4473 int length = end - start + 1;
4474 selectionModel.insertIndexInterval(start, length, true);
4475
4476 // If we have variable height rows, adjust the row model.
4477 if (rowModel != null) {
4478 rowModel.insertEntries(start, length, getRowHeight());
4479 }
4480 int rh = getRowHeight() ;
4481 Rectangle drawRect = new Rectangle(0, start * rh,
4482 getColumnModel().getTotalColumnWidth(),
4483 (getRowCount()-start) * rh);
4484
4485 revalidate();
4486 // PENDING(milne) revalidate calls repaint() if parent is a ScrollPane
4487 // repaint still required in the unusual case where there is no ScrollPane
4488 repaint(drawRect);
4489 }
4490
4491 /*
4492 * Invoked when rows have been removed from the table.
4493 * <p>
4494 * Application code will not use these methods explicitly, they
4495 * are used internally by JTable.
4496 *
4497 * @param e the TableModelEvent encapsulating the deletion
4498 */
4499 private void tableRowsDeleted(TableModelEvent e) {
4500 int start = e.getFirstRow();
4501 int end = e.getLastRow();
4502 if (start < 0) {
4503 start = 0;
4504 }
4505 if (end < 0) {
4506 end = getRowCount()-1;
4507 }
4508
4509 int deletedCount = end - start + 1;
4510 int previousRowCount = getRowCount() + deletedCount;
4511 // Adjust the selection to account for the new rows
4512 selectionModel.removeIndexInterval(start, end);
4513
4514 // If we have variable height rows, adjust the row model.
4515 if (rowModel != null) {
4516 rowModel.removeEntries(start, deletedCount);
4517 }
4518
4519 int rh = getRowHeight();
4520 Rectangle drawRect = new Rectangle(0, start * rh,
4521 getColumnModel().getTotalColumnWidth(),
4522 (previousRowCount - start) * rh);
4523
4524 revalidate();
4525 // PENDING(milne) revalidate calls repaint() if parent is a ScrollPane
4526 // repaint still required in the unusual case where there is no ScrollPane
4527 repaint(drawRect);
4528 }
4529
4530 //
4531 // Implementing TableColumnModelListener interface
4532 //
4533
4534 /**
4535 * Invoked when a column is added to the table column model.
4536 * <p>
4537 * Application code will not use these methods explicitly, they
4538 * are used internally by JTable.
4539 *
4540 * @see TableColumnModelListener
4541 */
4542 public void columnAdded(TableColumnModelEvent e) {
4543 // If I'm currently editing, then I should stop editing
4544 if (isEditing()) {
4545 removeEditor();
4546 }
4547 resizeAndRepaint();
4548 }
4549
4550 /**
4551 * Invoked when a column is removed from the table column model.
4552 * <p>
4553 * Application code will not use these methods explicitly, they
4554 * are used internally by JTable.
4555 *
4556 * @see TableColumnModelListener
4557 */
4558 public void columnRemoved(TableColumnModelEvent e) {
4559 // If I'm currently editing, then I should stop editing
4560 if (isEditing()) {
4561 removeEditor();
4562 }
4563 resizeAndRepaint();
4564 }
4565
4566 /**
4567 * Invoked when a column is repositioned. If a cell is being
4568 * edited, then editing is stopped and the cell is redrawn.
4569 * <p>
4570 * Application code will not use these methods explicitly, they
4571 * are used internally by JTable.
4572 *
4573 * @param e the event received
4574 * @see TableColumnModelListener
4575 */
4576 public void columnMoved(TableColumnModelEvent e) {
4577 if (isEditing() && !getCellEditor().stopCellEditing()) {
4578 getCellEditor().cancelCellEditing();
4579 }
4580 repaint();
4581 }
4582
4583 /**
4584 * Invoked when a column is moved due to a margin change.
4585 * If a cell is being edited, then editing is stopped and the cell
4586 * is redrawn.
4587 * <p>
4588 * Application code will not use these methods explicitly, they
4589 * are used internally by JTable.
4590 *
4591 * @param e the event received
4592 * @see TableColumnModelListener
4593 */
4594 public void columnMarginChanged(ChangeEvent e) {
4595 if (isEditing() && !getCellEditor().stopCellEditing()) {
4596 getCellEditor().cancelCellEditing();
4597 }
4598 TableColumn resizingColumn = getResizingColumn();
4599 // Need to do this here, before the parent's
4600 // layout manager calls getPreferredSize().
4601 if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF) {
4602 resizingColumn.setPreferredWidth(resizingColumn.getWidth());
4603 }
4604 resizeAndRepaint();
4605 }
4606
4607 private int limit(int i, int a, int b) {
4608 return Math.min(b, Math.max(i, a));
4609 }
4610
4611 /**
4612 * Invoked when the selection model of the <code>TableColumnModel</code>
4613 * is changed.
4614 * <p>
4615 * Application code will not use these methods explicitly, they
4616 * are used internally by JTable.
4617 *
4618 * @param e the event received
4619 * @see TableColumnModelListener
4620 */
4621 public void columnSelectionChanged(ListSelectionEvent e) {
4622 boolean isAdjusting = e.getValueIsAdjusting();
4623 if (columnSelectionAdjusting && !isAdjusting) {
4624 // The assumption is that when the model is no longer adjusting
4625 // we will have already gotten all the changes, and therefore
4626 // don't need to do an additional paint.
4627 columnSelectionAdjusting = false;
4628 return;
4629 }
4630 columnSelectionAdjusting = isAdjusting;
4631 // The getCellRect() call will fail unless there is at least one row.
4632 if (getRowCount() <= 0 || getColumnCount() <= 0) {
4633 return;
4634 }
4635 int firstIndex = limit(e.getFirstIndex(), 0, getColumnCount()-1);
4636 int lastIndex = limit(e.getLastIndex(), 0, getColumnCount()-1);
4637 int minRow = 0;
4638 int maxRow = getRowCount() - 1;
4639 if (getRowSelectionAllowed()) {
4640 minRow = selectionModel.getMinSelectionIndex();
4641 maxRow = selectionModel.getMaxSelectionIndex();
4642 int leadRow = getAdjustedIndex(selectionModel.getLeadSelectionIndex(), true);
4643
4644 if (minRow == -1 || maxRow == -1) {
4645 if (leadRow == -1) {
4646 // nothing to repaint, return
4647 return;
4648 }
4649
4650 // only thing to repaint is the lead
4651 minRow = maxRow = leadRow;
4652 } else {
4653 // We need to consider more than just the range between
4654 // the min and max selected index. The lead row, which could
4655 // be outside this range, should be considered also.
4656 if (leadRow != -1) {
4657 minRow = Math.min(minRow, leadRow);
4658 maxRow = Math.max(maxRow, leadRow);
4659 }
4660 }
4661 }
4662 Rectangle firstColumnRect = getCellRect(minRow, firstIndex, false);
4663 Rectangle lastColumnRect = getCellRect(maxRow, lastIndex, false);
4664 Rectangle dirtyRegion = firstColumnRect.union(lastColumnRect);
4665 repaint(dirtyRegion);
4666 }
4667
4668 //
4669 // Implementing ListSelectionListener interface
4670 //
4671
4672 /**
4673 * Invoked when the row selection changes -- repaints to show the new
4674 * selection.
4675 * <p>
4676 * Application code will not use these methods explicitly, they
4677 * are used internally by JTable.
4678 *
4679 * @param e the event received
4680 * @see ListSelectionListener
4681 */
4682 public void valueChanged(ListSelectionEvent e) {
4683 if (sortManager != null) {
4684 sortManager.viewSelectionChanged(e);
4685 }
4686 boolean isAdjusting = e.getValueIsAdjusting();
4687 if (rowSelectionAdjusting && !isAdjusting) {
4688 // The assumption is that when the model is no longer adjusting
4689 // we will have already gotten all the changes, and therefore
4690 // don't need to do an additional paint.
4691 rowSelectionAdjusting = false;
4692 return;
4693 }
4694 rowSelectionAdjusting = isAdjusting;
4695 // The getCellRect() calls will fail unless there is at least one column.
4696 if (getRowCount() <= 0 || getColumnCount() <= 0) {
4697 return;
4698 }
4699 int firstIndex = limit(e.getFirstIndex(), 0, getRowCount()-1);
4700 int lastIndex = limit(e.getLastIndex(), 0, getRowCount()-1);
4701 Rectangle firstRowRect = getCellRect(firstIndex, 0, false);
4702 Rectangle lastRowRect = getCellRect(lastIndex, getColumnCount()-1, false);
4703 Rectangle dirtyRegion = firstRowRect.union(lastRowRect);
4704 repaint(dirtyRegion);
4705 }
4706
4707 //
4708 // Implementing the CellEditorListener interface
4709 //
4710
4711 /**
4712 * Invoked when editing is finished. The changes are saved and the
4713 * editor is discarded.
4714 * <p>
4715 * Application code will not use these methods explicitly, they
4716 * are used internally by JTable.
4717 *
4718 * @param e the event received
4719 * @see CellEditorListener
4720 */
4721 public void editingStopped(ChangeEvent e) {
4722 // Take in the new value
4723 TableCellEditor editor = getCellEditor();
4724 if (editor != null) {
4725 Object value = editor.getCellEditorValue();
4726 setValueAt(value, editingRow, editingColumn);
4727 removeEditor();
4728 }
4729 }
4730
4731 /**
4732 * Invoked when editing is canceled. The editor object is discarded
4733 * and the cell is rendered once again.
4734 * <p>
4735 * Application code will not use these methods explicitly, they
4736 * are used internally by JTable.
4737 *
4738 * @param e the event received
4739 * @see CellEditorListener
4740 */
4741 public void editingCanceled(ChangeEvent e) {
4742 removeEditor();
4743 }
4744
4745 //
4746 // Implementing the Scrollable interface
4747 //
4748
4749 /**
4750 * Sets the preferred size of the viewport for this table.
4751 *
4752 * @param size a <code>Dimension</code> object specifying the <code>preferredSize</code> of a
4753 * <code>JViewport</code> whose view is this table
4754 * @see Scrollable#getPreferredScrollableViewportSize
4755 * @beaninfo
4756 * description: The preferred size of the viewport.
4757 */
4758 public void setPreferredScrollableViewportSize(Dimension size) {
4759 preferredViewportSize = size;
4760 }
4761
4762 /**
4763 * Returns the preferred size of the viewport for this table.
4764 *
4765 * @return a <code>Dimension</code> object containing the <code>preferredSize</code> of the <code>JViewport</code>
4766 * which displays this table
4767 * @see Scrollable#getPreferredScrollableViewportSize
4768 */
4769 public Dimension getPreferredScrollableViewportSize() {
4770 return preferredViewportSize;
4771 }
4772
4773 /**
4774 * Returns the scroll increment (in pixels) that completely exposes one new
4775 * row or column (depending on the orientation).
4776 * <p>
4777 * This method is called each time the user requests a unit scroll.
4778 *
4779 * @param visibleRect the view area visible within the viewport
4780 * @param orientation either <code>SwingConstants.VERTICAL</code>
4781 * or <code>SwingConstants.HORIZONTAL</code>
4782 * @param direction less than zero to scroll up/left,
4783 * greater than zero for down/right
4784 * @return the "unit" increment for scrolling in the specified direction
4785 * @see Scrollable#getScrollableUnitIncrement
4786 */
4787 public int getScrollableUnitIncrement(Rectangle visibleRect,
4788 int orientation,
4789 int direction) {
4790 int leadingRow;
4791 int leadingCol;
4792 Rectangle leadingCellRect;
4793
4794 int leadingVisibleEdge;
4795 int leadingCellEdge;
4796 int leadingCellSize;
4797
4798 leadingRow = getLeadingRow(visibleRect);
4799 leadingCol = getLeadingCol(visibleRect);
4800 if (orientation == SwingConstants.VERTICAL && leadingRow < 0) {
4801 // Couldn't find leading row - return some default value
4802 return getRowHeight();
4803 }
4804 else if (orientation == SwingConstants.HORIZONTAL && leadingCol < 0) {
4805 // Couldn't find leading col - return some default value
4806 return 100;
4807 }
4808
4809 // Note that it's possible for one of leadingCol or leadingRow to be
4810 // -1, depending on the orientation. This is okay, as getCellRect()
4811 // still provides enough information to calculate the unit increment.
4812 leadingCellRect = getCellRect(leadingRow, leadingCol, true);
4813 leadingVisibleEdge = leadingEdge(visibleRect, orientation);
4814 leadingCellEdge = leadingEdge(leadingCellRect, orientation);
4815
4816 if (orientation == SwingConstants.VERTICAL) {
4817 leadingCellSize = leadingCellRect.height;
4818
4819 }
4820 else {
4821 leadingCellSize = leadingCellRect.width;
4822 }
4823
4824 // 4 cases:
4825 // #1: Leading cell fully visible, reveal next cell
4826 // #2: Leading cell fully visible, hide leading cell
4827 // #3: Leading cell partially visible, hide rest of leading cell
4828 // #4: Leading cell partially visible, reveal rest of leading cell
4829
4830 if (leadingVisibleEdge == leadingCellEdge) { // Leading cell is fully
4831 // visible
4832 // Case #1: Reveal previous cell
4833 if (direction < 0) {
4834 int retVal = 0;
4835
4836 if (orientation == SwingConstants.VERTICAL) {
4837 // Loop past any zero-height rows
4838 while (--leadingRow >= 0) {
4839 retVal = getRowHeight(leadingRow);
4840 if (retVal != 0) {
4841 break;
4842 }
4843 }
4844 }
4845 else { // HORIZONTAL
4846 // Loop past any zero-width cols
4847 while (--leadingCol >= 0) {
4848 retVal = getCellRect(leadingRow, leadingCol, true).width;
4849 if (retVal != 0) {
4850 break;
4851 }
4852 }
4853 }
4854 return retVal;
4855 }
4856 else { // Case #2: hide leading cell
4857 return leadingCellSize;
4858 }
4859 }
4860 else { // Leading cell is partially hidden
4861 // Compute visible, hidden portions
4862 int hiddenAmt = Math.abs(leadingVisibleEdge - leadingCellEdge);
4863 int visibleAmt = leadingCellSize - hiddenAmt;
4864
4865 if (direction > 0) {
4866 // Case #3: hide showing portion of leading cell
4867 return visibleAmt;
4868 }
4869 else { // Case #4: reveal hidden portion of leading cell
4870 return hiddenAmt;
4871 }
4872 }
4873 }
4874
4875 /**
4876 * Returns <code>visibleRect.height</code> or
4877 * <code>visibleRect.width</code>,
4878 * depending on this table's orientation. Note that as of Swing 1.1.1
4879 * (Java 2 v 1.2.2) the value
4880 * returned will ensure that the viewport is cleanly aligned on
4881 * a row boundary.
4882 *
4883 * @return <code>visibleRect.height</code> or
4884 * <code>visibleRect.width</code>
4885 * per the orientation
4886 * @see Scrollable#getScrollableBlockIncrement
4887 */
4888 public int getScrollableBlockIncrement(Rectangle visibleRect,
4889 int orientation, int direction) {
4890
4891 if (getRowCount() == 0) {
4892 // Short-circuit empty table model
4893 if (SwingConstants.VERTICAL == orientation) {
4894 int rh = getRowHeight();
4895 return (rh > 0) ? Math.max(rh, (visibleRect.height / rh) * rh) :
4896 visibleRect.height;
4897 }
4898 else {
4899 return visibleRect.width;
4900 }
4901 }
4902 // Shortcut for vertical scrolling of a table w/ uniform row height
4903 if (null == rowModel && SwingConstants.VERTICAL == orientation) {
4904 int row = rowAtPoint(visibleRect.getLocation());
4905 assert row != -1;
4906 int col = columnAtPoint(visibleRect.getLocation());
4907 Rectangle cellRect = getCellRect(row, col, true);
4908
4909 if (cellRect.y == visibleRect.y) {
4910 int rh = getRowHeight();
4911 assert rh > 0;
4912 return Math.max(rh, (visibleRect.height / rh) * rh);
4913 }
4914 }
4915 if (direction < 0) {
4916 return getPreviousBlockIncrement(visibleRect, orientation);
4917 }
4918 else {
4919 return getNextBlockIncrement(visibleRect, orientation);
4920 }
4921 }
4922
4923 /**
4924 * Called to get the block increment for upward scrolling in cases of
4925 * horizontal scrolling, or for vertical scrolling of a table with
4926 * variable row heights.
4927 */
4928 private int getPreviousBlockIncrement(Rectangle visibleRect,
4929 int orientation) {
4930 // Measure back from visible leading edge
4931 // If we hit the cell on its leading edge, it becomes the leading cell.
4932 // Else, use following cell
4933
4934 int row;
4935 int col;
4936
4937 int newEdge;
4938 Point newCellLoc;
4939
4940 int visibleLeadingEdge = leadingEdge(visibleRect, orientation);
4941 boolean leftToRight = getComponentOrientation().isLeftToRight();
4942 int newLeadingEdge;
4943
4944 // Roughly determine the new leading edge by measuring back from the
4945 // leading visible edge by the size of the visible rect, and find the
4946 // cell there.
4947 if (orientation == SwingConstants.VERTICAL) {
4948 newEdge = visibleLeadingEdge - visibleRect.height;
4949 int x = visibleRect.x + (leftToRight ? 0 : visibleRect.width);
4950 newCellLoc = new Point(x, newEdge);
4951 }
4952 else if (leftToRight) {
4953 newEdge = visibleLeadingEdge - visibleRect.width;
4954 newCellLoc = new Point(newEdge, visibleRect.y);
4955 }
4956 else { // Horizontal, right-to-left
4957 newEdge = visibleLeadingEdge + visibleRect.width;
4958 newCellLoc = new Point(newEdge - 1, visibleRect.y);
4959 }
4960 row = rowAtPoint(newCellLoc);
4961 col = columnAtPoint(newCellLoc);
4962
4963 // If we're measuring past the beginning of the table, we get an invalid
4964 // cell. Just go to the beginning of the table in this case.
4965 if (orientation == SwingConstants.VERTICAL & row < 0) {
4966 newLeadingEdge = 0;
4967 }
4968 else if (orientation == SwingConstants.HORIZONTAL & col < 0) {
4969 if (leftToRight) {
4970 newLeadingEdge = 0;
4971 }
4972 else {
4973 newLeadingEdge = getWidth();
4974 }
4975 }
4976 else {
4977 // Refine our measurement
4978 Rectangle newCellRect = getCellRect(row, col, true);
4979 int newCellLeadingEdge = leadingEdge(newCellRect, orientation);
4980 int newCellTrailingEdge = trailingEdge(newCellRect, orientation);
4981
4982 // Usually, we hit in the middle of newCell, and want to scroll to
4983 // the beginning of the cell after newCell. But there are a
4984 // couple corner cases where we want to scroll to the beginning of
4985 // newCell itself. These cases are:
4986 // 1) newCell is so large that it ends at or extends into the
4987 // visibleRect (newCell is the leading cell, or is adjacent to
4988 // the leading cell)
4989 // 2) newEdge happens to fall right on the beginning of a cell
4990
4991 // Case 1
4992 if ((orientation == SwingConstants.VERTICAL || leftToRight) &&
4993 (newCellTrailingEdge >= visibleLeadingEdge)) {
4994 newLeadingEdge = newCellLeadingEdge;
4995 }
4996 else if (orientation == SwingConstants.HORIZONTAL &&
4997 !leftToRight &&
4998 newCellTrailingEdge <= visibleLeadingEdge) {
4999 newLeadingEdge = newCellLeadingEdge;
5000 }
5001 // Case 2:
5002 else if (newEdge == newCellLeadingEdge) {
5003 newLeadingEdge = newCellLeadingEdge;
5004 }
5005 // Common case: scroll to cell after newCell
5006 else {
5007 newLeadingEdge = newCellTrailingEdge;
5008 }
5009 }
5010 return Math.abs(visibleLeadingEdge - newLeadingEdge);
5011 }
5012
5013 /**
5014 * Called to get the block increment for downward scrolling in cases of
5015 * horizontal scrolling, or for vertical scrolling of a table with
5016 * variable row heights.
5017 */
5018 private int getNextBlockIncrement(Rectangle visibleRect,
5019 int orientation) {
5020 // Find the cell at the trailing edge. Return the distance to put
5021 // that cell at the leading edge.
5022 int trailingRow = getTrailingRow(visibleRect);
5023 int trailingCol = getTrailingCol(visibleRect);
5024
5025 Rectangle cellRect;
5026 boolean cellFillsVis;
5027
5028 int cellLeadingEdge;
5029 int cellTrailingEdge;
5030 int newLeadingEdge;
5031 int visibleLeadingEdge = leadingEdge(visibleRect, orientation);
5032
5033 // If we couldn't find trailing cell, just return the size of the
5034 // visibleRect. Note that, for instance, we don't need the
5035 // trailingCol to proceed if we're scrolling vertically, because
5036 // cellRect will still fill in the required dimensions. This would
5037 // happen if we're scrolling vertically, and the table is not wide
5038 // enough to fill the visibleRect.
5039 if (orientation == SwingConstants.VERTICAL && trailingRow < 0) {
5040 return visibleRect.height;
5041 }
5042 else if (orientation == SwingConstants.HORIZONTAL && trailingCol < 0) {
5043 return visibleRect.width;
5044 }
5045 cellRect = getCellRect(trailingRow, trailingCol, true);
5046 cellLeadingEdge = leadingEdge(cellRect, orientation);
5047 cellTrailingEdge = trailingEdge(cellRect, orientation);
5048
5049 if (orientation == SwingConstants.VERTICAL ||
5050 getComponentOrientation().isLeftToRight()) {
5051 cellFillsVis = cellLeadingEdge <= visibleLeadingEdge;
5052 }
5053 else { // Horizontal, right-to-left
5054 cellFillsVis = cellLeadingEdge >= visibleLeadingEdge;
5055 }
5056
5057 if (cellFillsVis) {
5058 // The visibleRect contains a single large cell. Scroll to the end
5059 // of this cell, so the following cell is the first cell.
5060 newLeadingEdge = cellTrailingEdge;
5061 }
5062 else if (cellTrailingEdge == trailingEdge(visibleRect, orientation)) {
5063 // The trailing cell happens to end right at the end of the
5064 // visibleRect. Again, scroll to the beginning of the next cell.
5065 newLeadingEdge = cellTrailingEdge;
5066 }
5067 else {
5068 // Common case: the trailing cell is partially visible, and isn't
5069 // big enough to take up the entire visibleRect. Scroll so it
5070 // becomes the leading cell.
5071 newLeadingEdge = cellLeadingEdge;
5072 }
5073 return Math.abs(newLeadingEdge - visibleLeadingEdge);
5074 }
5075
5076 /*
5077 * Return the row at the top of the visibleRect
5078 *
5079 * May return -1
5080 */
5081 private int getLeadingRow(Rectangle visibleRect) {
5082 Point leadingPoint;
5083
5084 if (getComponentOrientation().isLeftToRight()) {
5085 leadingPoint = new Point(visibleRect.x, visibleRect.y);
5086 }
5087 else {
5088 leadingPoint = new Point(visibleRect.x + visibleRect.width - 1,
5089 visibleRect.y);
5090 }
5091 return rowAtPoint(leadingPoint);
5092 }
5093
5094 /*
5095 * Return the column at the leading edge of the visibleRect.
5096 *
5097 * May return -1
5098 */
5099 private int getLeadingCol(Rectangle visibleRect) {
5100 Point leadingPoint;
5101
5102 if (getComponentOrientation().isLeftToRight()) {
5103 leadingPoint = new Point(visibleRect.x, visibleRect.y);
5104 }
5105 else {
5106 leadingPoint = new Point(visibleRect.x + visibleRect.width - 1,
5107 visibleRect.y);
5108 }
5109 return columnAtPoint(leadingPoint);
5110 }
5111
5112 /*
5113 * Return the row at the bottom of the visibleRect.
5114 *
5115 * May return -1
5116 */
5117 private int getTrailingRow(Rectangle visibleRect) {
5118 Point trailingPoint;
5119
5120 if (getComponentOrientation().isLeftToRight()) {
5121 trailingPoint = new Point(visibleRect.x,
5122 visibleRect.y + visibleRect.height - 1);
5123 }
5124 else {
5125 trailingPoint = new Point(visibleRect.x + visibleRect.width - 1,
5126 visibleRect.y + visibleRect.height - 1);
5127 }
5128 return rowAtPoint(trailingPoint);
5129 }
5130
5131 /*
5132 * Return the column at the trailing edge of the visibleRect.
5133 *
5134 * May return -1
5135 */
5136 private int getTrailingCol(Rectangle visibleRect) {
5137 Point trailingPoint;
5138
5139 if (getComponentOrientation().isLeftToRight()) {
5140 trailingPoint = new Point(visibleRect.x + visibleRect.width - 1,
5141 visibleRect.y);
5142 }
5143 else {
5144 trailingPoint = new Point(visibleRect.x, visibleRect.y);
5145 }
5146 return columnAtPoint(trailingPoint);
5147 }
5148
5149 /*
5150 * Returns the leading edge ("beginning") of the given Rectangle.
5151 * For VERTICAL, this is the top, for left-to-right, the left side, and for
5152 * right-to-left, the right side.
5153 */
5154 private int leadingEdge(Rectangle rect, int orientation) {
5155 if (orientation == SwingConstants.VERTICAL) {
5156 return rect.y;
5157 }
5158 else if (getComponentOrientation().isLeftToRight()) {
5159 return rect.x;
5160 }
5161 else { // Horizontal, right-to-left
5162 return rect.x + rect.width;
5163 }
5164 }
5165
5166 /*
5167 * Returns the trailing edge ("end") of the given Rectangle.
5168 * For VERTICAL, this is the bottom, for left-to-right, the right side, and
5169 * for right-to-left, the left side.
5170 */
5171 private int trailingEdge(Rectangle rect, int orientation) {
5172 if (orientation == SwingConstants.VERTICAL) {
5173 return rect.y + rect.height;
5174 }
5175 else if (getComponentOrientation().isLeftToRight()) {
5176 return rect.x + rect.width;
5177 }
5178 else { // Horizontal, right-to-left
5179 return rect.x;
5180 }
5181 }
5182
5183 /**
5184 * Returns false if <code>autoResizeMode</code> is set to
5185 * <code>AUTO_RESIZE_OFF</code>, which indicates that the
5186 * width of the viewport does not determine the width
5187 * of the table. Otherwise returns true.
5188 *
5189 * @return false if <code>autoResizeMode</code> is set
5190 * to <code>AUTO_RESIZE_OFF</code>, otherwise returns true
5191 * @see Scrollable#getScrollableTracksViewportWidth
5192 */
5193 public boolean getScrollableTracksViewportWidth() {
5194 return !(autoResizeMode == AUTO_RESIZE_OFF);
5195 }
5196
5197 /**
5198 * Returns {@code false} to indicate that the height of the viewport does
5199 * not determine the height of the table, unless
5200 * {@code getFillsViewportHeight} is {@code true} and the preferred height
5201 * of the table is smaller than the viewport's height.
5202 *
5203 * @return {@code false} unless {@code getFillsViewportHeight} is
5204 * {@code true} and the table needs to be stretched to fill
5205 * the viewport
5206 * @see Scrollable#getScrollableTracksViewportHeight
5207 * @see #setFillsViewportHeight
5208 * @see #getFillsViewportHeight
5209 */
5210 public boolean getScrollableTracksViewportHeight() {
5211 Container parent = SwingUtilities.getUnwrappedParent(this);
5212 return getFillsViewportHeight()
5213 && parent instanceof JViewport
5214 && parent.getHeight() > getPreferredSize().height;
5215 }
5216
5217 /**
5218 * Sets whether or not this table is always made large enough
5219 * to fill the height of an enclosing viewport. If the preferred
5220 * height of the table is smaller than the viewport, then the table
5221 * will be stretched to fill the viewport. In other words, this
5222 * ensures the table is never smaller than the viewport.
5223 * The default for this property is {@code false}.
5224 *
5225 * @param fillsViewportHeight whether or not this table is always
5226 * made large enough to fill the height of an enclosing
5227 * viewport
5228 * @see #getFillsViewportHeight
5229 * @see #getScrollableTracksViewportHeight
5230 * @since 1.6
5231 * @beaninfo
5232 * bound: true
5233 * description: Whether or not this table is always made large enough
5234 * to fill the height of an enclosing viewport
5235 */
5236 public void setFillsViewportHeight(boolean fillsViewportHeight) {
5237 boolean old = this.fillsViewportHeight;
5238 this.fillsViewportHeight = fillsViewportHeight;
5239 resizeAndRepaint();
5240 firePropertyChange("fillsViewportHeight", old, fillsViewportHeight);
5241 }
5242
5243 /**
5244 * Returns whether or not this table is always made large enough
5245 * to fill the height of an enclosing viewport.
5246 *
5247 * @return whether or not this table is always made large enough
5248 * to fill the height of an enclosing viewport
5249 * @see #setFillsViewportHeight
5250 * @since 1.6
5251 */
5252 public boolean getFillsViewportHeight() {
5253 return fillsViewportHeight;
5254 }
5255
5256 //
5257 // Protected Methods
5258 //
5259
5260 protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
5261 int condition, boolean pressed) {
5262 boolean retValue = super.processKeyBinding(ks, e, condition, pressed);
5263
5264 // Start editing when a key is typed. UI classes can disable this behavior
5265 // by setting the client property JTable.autoStartsEdit to Boolean.FALSE.
5266 if (!retValue && condition == WHEN_ANCESTOR_OF_FOCUSED_COMPONENT &&
5267 isFocusOwner() &&
5268 !Boolean.FALSE.equals(getClientProperty("JTable.autoStartsEdit"))) {
5269 // We do not have a binding for the event.
5270 Component editorComponent = getEditorComponent();
5271 if (editorComponent == null) {
5272 // Only attempt to install the editor on a KEY_PRESSED,
5273 if (e == null || e.getID() != KeyEvent.KEY_PRESSED) {
5274 return false;
5275 }
5276 // Don't start when just a modifier is pressed
5277 int code = e.getKeyCode();
5278 if (code == KeyEvent.VK_SHIFT || code == KeyEvent.VK_CONTROL ||
5279 code == KeyEvent.VK_ALT) {
5280 return false;
5281 }
5282 // Try to install the editor
5283 int leadRow = getSelectionModel().getLeadSelectionIndex();
5284 int leadColumn = getColumnModel().getSelectionModel().
5285 getLeadSelectionIndex();
5286 if (leadRow != -1 && leadColumn != -1 && !isEditing()) {
5287 if (!editCellAt(leadRow, leadColumn, e)) {
5288 return false;
5289 }
5290 }
5291 editorComponent = getEditorComponent();
5292 if (editorComponent == null) {
5293 return false;
5294 }
5295 }
5296 // If the editorComponent is a JComponent, pass the event to it.
5297 if (editorComponent instanceof JComponent) {
5298 retValue = ((JComponent)editorComponent).processKeyBinding
5299 (ks, e, WHEN_FOCUSED, pressed);
5300 // If we have started an editor as a result of the user
5301 // pressing a key and the surrendersFocusOnKeystroke property
5302 // is true, give the focus to the new editor.
5303 if (getSurrendersFocusOnKeystroke()) {
5304 editorComponent.requestFocus();
5305 }
5306 }
5307 }
5308 return retValue;
5309 }
5310
5311 private void setLazyValue(Hashtable h, Class c, String s) {
5312 h.put(c, new SwingLazyValue(s));
5313 }
5314
5315 private void setLazyRenderer(Class c, String s) {
5316 setLazyValue(defaultRenderersByColumnClass, c, s);
5317 }
5318
5319 /**
5320 * Creates default cell renderers for objects, numbers, doubles, dates,
5321 * booleans, and icons.
5322 * @see javax.swing.table.DefaultTableCellRenderer
5323 *
5324 */
5325 protected void createDefaultRenderers() {
5326 defaultRenderersByColumnClass = new UIDefaults(8, 0.75f);
5327
5328 // Objects
5329 setLazyRenderer(Object.class, "javax.swing.table.DefaultTableCellRenderer$UIResource");
5330
5331 // Numbers
5332 setLazyRenderer(Number.class, "javax.swing.JTable$NumberRenderer");
5333
5334 // Doubles and Floats
5335 setLazyRenderer(Float.class, "javax.swing.JTable$DoubleRenderer");
5336 setLazyRenderer(Double.class, "javax.swing.JTable$DoubleRenderer");
5337
5338 // Dates
5339 setLazyRenderer(Date.class, "javax.swing.JTable$DateRenderer");
5340
5341 // Icons and ImageIcons
5342 setLazyRenderer(Icon.class, "javax.swing.JTable$IconRenderer");
5343 setLazyRenderer(ImageIcon.class, "javax.swing.JTable$IconRenderer");
5344
5345 // Booleans
5346 setLazyRenderer(Boolean.class, "javax.swing.JTable$BooleanRenderer");
5347 }
5348
5349 /**
5350 * Default Renderers
5351 **/
5352 static class NumberRenderer extends DefaultTableCellRenderer.UIResource {
5353 public NumberRenderer() {
5354 super();
5355 setHorizontalAlignment(JLabel.RIGHT);
5356 }
5357 }
5358
5359 static class DoubleRenderer extends NumberRenderer {
5360 NumberFormat formatter;
5361 public DoubleRenderer() { super(); }
5362
5363 public void setValue(Object value) {
5364 if (formatter == null) {
5365 formatter = NumberFormat.getInstance();
5366 }
5367 setText((value == null) ? "" : formatter.format(value));
5368 }
5369 }
5370
5371 static class DateRenderer extends DefaultTableCellRenderer.UIResource {
5372 DateFormat formatter;
5373 public DateRenderer() { super(); }
5374
5375 public void setValue(Object value) {
5376 if (formatter==null) {
5377 formatter = DateFormat.getDateInstance();
5378 }
5379 setText((value == null) ? "" : formatter.format(value));
5380 }
5381 }
5382
5383 static class IconRenderer extends DefaultTableCellRenderer.UIResource {
5384 public IconRenderer() {
5385 super();
5386 setHorizontalAlignment(JLabel.CENTER);
5387 }
5388 public void setValue(Object value) { setIcon((value instanceof Icon) ? (Icon)value : null); }
5389 }
5390
5391
5392 static class BooleanRenderer extends JCheckBox implements TableCellRenderer, UIResource
5393 {
5394 private static final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
5395
5396 public BooleanRenderer() {
5397 super();
5398 setHorizontalAlignment(JLabel.CENTER);
5399 setBorderPainted(true);
5400 }
5401
5402 public Component getTableCellRendererComponent(JTable table, Object value,
5403 boolean isSelected, boolean hasFocus, int row, int column) {
5404 if (isSelected) {
5405 setForeground(table.getSelectionForeground());
5406 super.setBackground(table.getSelectionBackground());
5407 }
5408 else {
5409 setForeground(table.getForeground());
5410 setBackground(table.getBackground());
5411 }
5412 setSelected((value != null && ((Boolean)value).booleanValue()));
5413
5414 if (hasFocus) {
5415 setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
5416 } else {
5417 setBorder(noFocusBorder);
5418 }
5419
5420 return this;
5421 }
5422 }
5423
5424 private void setLazyEditor(Class c, String s) {
5425 setLazyValue(defaultEditorsByColumnClass, c, s);
5426 }
5427
5428 /**
5429 * Creates default cell editors for objects, numbers, and boolean values.
5430 * @see DefaultCellEditor
5431 */
5432 protected void createDefaultEditors() {
5433 defaultEditorsByColumnClass = new UIDefaults(3, 0.75f);
5434
5435 // Objects
5436 setLazyEditor(Object.class, "javax.swing.JTable$GenericEditor");
5437
5438 // Numbers
5439 setLazyEditor(Number.class, "javax.swing.JTable$NumberEditor");
5440
5441 // Booleans
5442 setLazyEditor(Boolean.class, "javax.swing.JTable$BooleanEditor");
5443 }
5444
5445 /**
5446 * Default Editors
5447 */
5448 static class GenericEditor extends DefaultCellEditor {
5449
5450 Class[] argTypes = new Class[]{String.class};
5451 java.lang.reflect.Constructor constructor;
5452 Object value;
5453
5454 public GenericEditor() {
5455 super(new JTextField());
5456 getComponent().setName("Table.editor");
5457 }
5458
5459 public boolean stopCellEditing() {
5460 String s = (String)super.getCellEditorValue();
5461 // Here we are dealing with the case where a user
5462 // has deleted the string value in a cell, possibly
5463 // after a failed validation. Return null, so that
5464 // they have the option to replace the value with
5465 // null or use escape to restore the original.
5466 // For Strings, return "" for backward compatibility.
5467 if ("".equals(s)) {
5468 if (constructor.getDeclaringClass() == String.class) {
5469 value = s;
5470 }
5471 super.stopCellEditing();
5472 }
5473
5474 try {
5475 value = constructor.newInstance(new Object[]{s});
5476 }
5477 catch (Exception e) {
5478 ((JComponent)getComponent()).setBorder(new LineBorder(Color.red));
5479 return false;
5480 }
5481 return super.stopCellEditing();
5482 }
5483
5484 public Component getTableCellEditorComponent(JTable table, Object value,
5485 boolean isSelected,
5486 int row, int column) {
5487 this.value = null;
5488 ((JComponent)getComponent()).setBorder(new LineBorder(Color.black));
5489 try {
5490 Class<?> type = table.getColumnClass(column);
5491 // Since our obligation is to produce a value which is
5492 // assignable for the required type it is OK to use the
5493 // String constructor for columns which are declared
5494 // to contain Objects. A String is an Object.
5495 if (type == Object.class) {
5496 type = String.class;
5497 }
5498 constructor = type.getConstructor(argTypes);
5499 }
5500 catch (Exception e) {
5501 return null;
5502 }
5503 return super.getTableCellEditorComponent(table, value, isSelected, row, column);
5504 }
5505
5506 public Object getCellEditorValue() {
5507 return value;
5508 }
5509 }
5510
5511 static class NumberEditor extends GenericEditor {
5512
5513 public NumberEditor() {
5514 ((JTextField)getComponent()).setHorizontalAlignment(JTextField.RIGHT);
5515 }
5516 }
5517
5518 static class BooleanEditor extends DefaultCellEditor {
5519 public BooleanEditor() {
5520 super(new JCheckBox());
5521 JCheckBox checkBox = (JCheckBox)getComponent();
5522 checkBox.setHorizontalAlignment(JCheckBox.CENTER);
5523 }
5524 }
5525
5526 /**
5527 * Initializes table properties to their default values.
5528 */
5529 protected void initializeLocalVars() {
5530 updateSelectionOnSort = true;
5531 setOpaque(true);
5532 createDefaultRenderers();
5533 createDefaultEditors();
5534
5535 setTableHeader(createDefaultTableHeader());
5536
5537 setShowGrid(true);
5538 setAutoResizeMode(AUTO_RESIZE_SUBSEQUENT_COLUMNS);
5539 setRowHeight(16);
5540 isRowHeightSet = false;
5541 setRowMargin(1);
5542 setRowSelectionAllowed(true);
5543 setCellEditor(null);
5544 setEditingColumn(-1);
5545 setEditingRow(-1);
5546 setSurrendersFocusOnKeystroke(false);
5547 setPreferredScrollableViewportSize(new Dimension(450, 400));
5548
5549 // I'm registered to do tool tips so we can draw tips for the renderers
5550 ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
5551 toolTipManager.registerComponent(this);
5552
5553 setAutoscrolls(true);
5554 }
5555
5556 /**
5557 * Returns the default table model object, which is
5558 * a <code>DefaultTableModel</code>. A subclass can override this
5559 * method to return a different table model object.
5560 *
5561 * @return the default table model object
5562 * @see javax.swing.table.DefaultTableModel
5563 */
5564 protected TableModel createDefaultDataModel() {
5565 return new DefaultTableModel();
5566 }
5567
5568 /**
5569 * Returns the default column model object, which is
5570 * a <code>DefaultTableColumnModel</code>. A subclass can override this
5571 * method to return a different column model object.
5572 *
5573 * @return the default column model object
5574 * @see javax.swing.table.DefaultTableColumnModel
5575 */
5576 protected TableColumnModel createDefaultColumnModel() {
5577 return new DefaultTableColumnModel();
5578 }
5579
5580 /**
5581 * Returns the default selection model object, which is
5582 * a <code>DefaultListSelectionModel</code>. A subclass can override this
5583 * method to return a different selection model object.
5584 *
5585 * @return the default selection model object
5586 * @see javax.swing.DefaultListSelectionModel
5587 */
5588 protected ListSelectionModel createDefaultSelectionModel() {
5589 return new DefaultListSelectionModel();
5590 }
5591
5592 /**
5593 * Returns the default table header object, which is
5594 * a <code>JTableHeader</code>. A subclass can override this
5595 * method to return a different table header object.
5596 *
5597 * @return the default table header object
5598 * @see javax.swing.table.JTableHeader
5599 */
5600 protected JTableHeader createDefaultTableHeader() {
5601 return new JTableHeader(columnModel);
5602 }
5603
5604 /**
5605 * Equivalent to <code>revalidate</code> followed by <code>repaint</code>.
5606 */
5607 protected void resizeAndRepaint() {
5608 revalidate();
5609 repaint();
5610 }
5611
5612 /**
5613 * Returns the active cell editor, which is {@code null} if the table
5614 * is not currently editing.
5615 *
5616 * @return the {@code TableCellEditor} that does the editing,
5617 * or {@code null} if the table is not currently editing.
5618 * @see #cellEditor
5619 * @see #getCellEditor(int, int)
5620 */
5621 public TableCellEditor getCellEditor() {
5622 return cellEditor;
5623 }
5624
5625 /**
5626 * Sets the active cell editor.
5627 *
5628 * @param anEditor the active cell editor
5629 * @see #cellEditor
5630 * @beaninfo
5631 * bound: true
5632 * description: The table's active cell editor.
5633 */
5634 public void setCellEditor(TableCellEditor anEditor) {
5635 TableCellEditor oldEditor = cellEditor;
5636 cellEditor = anEditor;
5637 firePropertyChange("tableCellEditor", oldEditor, anEditor);
5638 }
5639
5640 /**
5641 * Sets the <code>editingColumn</code> variable.
5642 * @param aColumn the column of the cell to be edited
5643 *
5644 * @see #editingColumn
5645 */
5646 public void setEditingColumn(int aColumn) {
5647 editingColumn = aColumn;
5648 }
5649
5650 /**
5651 * Sets the <code>editingRow</code> variable.
5652 * @param aRow the row of the cell to be edited
5653 *
5654 * @see #editingRow
5655 */
5656 public void setEditingRow(int aRow) {
5657 editingRow = aRow;
5658 }
5659
5660 /**
5661 * Returns an appropriate renderer for the cell specified by this row and
5662 * column. If the <code>TableColumn</code> for this column has a non-null
5663 * renderer, returns that. If not, finds the class of the data in
5664 * this column (using <code>getColumnClass</code>)
5665 * and returns the default renderer for this type of data.
5666 * <p>
5667 * <b>Note:</b>
5668 * Throughout the table package, the internal implementations always
5669 * use this method to provide renderers so that this default behavior
5670 * can be safely overridden by a subclass.
5671 *
5672 * @param row the row of the cell to render, where 0 is the first row
5673 * @param column the column of the cell to render,
5674 * where 0 is the first column
5675 * @return the assigned renderer; if <code>null</code>
5676 * returns the default renderer
5677 * for this type of object
5678 * @see javax.swing.table.DefaultTableCellRenderer
5679 * @see javax.swing.table.TableColumn#setCellRenderer
5680 * @see #setDefaultRenderer
5681 */
5682 public TableCellRenderer getCellRenderer(int row, int column) {
5683 TableColumn tableColumn = getColumnModel().getColumn(column);
5684 TableCellRenderer renderer = tableColumn.getCellRenderer();
5685 if (renderer == null) {
5686 renderer = getDefaultRenderer(getColumnClass(column));
5687 }
5688 return renderer;
5689 }
5690
5691 /**
5692 * Prepares the renderer by querying the data model for the
5693 * value and selection state
5694 * of the cell at <code>row</code>, <code>column</code>.
5695 * Returns the component (may be a <code>Component</code>
5696 * or a <code>JComponent</code>) under the event location.
5697 * <p>
5698 * During a printing operation, this method will configure the
5699 * renderer without indicating selection or focus, to prevent
5700 * them from appearing in the printed output. To do other
5701 * customizations based on whether or not the table is being
5702 * printed, you can check the value of
5703 * {@link javax.swing.JComponent#isPaintingForPrint()}, either here
5704 * or within custom renderers.
5705 * <p>
5706 * <b>Note:</b>
5707 * Throughout the table package, the internal implementations always
5708 * use this method to prepare renderers so that this default behavior
5709 * can be safely overridden by a subclass.
5710 *
5711 * @param renderer the <code>TableCellRenderer</code> to prepare
5712 * @param row the row of the cell to render, where 0 is the first row
5713 * @param column the column of the cell to render,
5714 * where 0 is the first column
5715 * @return the <code>Component</code> under the event location
5716 */
5717 public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
5718 Object value = getValueAt(row, column);
5719
5720 boolean isSelected = false;
5721 boolean hasFocus = false;
5722
5723 // Only indicate the selection and focused cell if not printing
5724 if (!isPaintingForPrint()) {
5725 isSelected = isCellSelected(row, column);
5726
5727 boolean rowIsLead =
5728 (selectionModel.getLeadSelectionIndex() == row);
5729 boolean colIsLead =
5730 (columnModel.getSelectionModel().getLeadSelectionIndex() == column);
5731
5732 hasFocus = (rowIsLead && colIsLead) && isFocusOwner();
5733 }
5734
5735 return renderer.getTableCellRendererComponent(this, value,
5736 isSelected, hasFocus,
5737 row, column);
5738 }
5739
5740 /**
5741 * Returns an appropriate editor for the cell specified by
5742 * <code>row</code> and <code>column</code>. If the
5743 * <code>TableColumn</code> for this column has a non-null editor,
5744 * returns that. If not, finds the class of the data in this
5745 * column (using <code>getColumnClass</code>)
5746 * and returns the default editor for this type of data.
5747 * <p>
5748 * <b>Note:</b>
5749 * Throughout the table package, the internal implementations always
5750 * use this method to provide editors so that this default behavior
5751 * can be safely overridden by a subclass.
5752 *
5753 * @param row the row of the cell to edit, where 0 is the first row
5754 * @param column the column of the cell to edit,
5755 * where 0 is the first column
5756 * @return the editor for this cell;
5757 * if <code>null</code> return the default editor for
5758 * this type of cell
5759 * @see DefaultCellEditor
5760 */
5761 public TableCellEditor getCellEditor(int row, int column) {
5762 TableColumn tableColumn = getColumnModel().getColumn(column);
5763 TableCellEditor editor = tableColumn.getCellEditor();
5764 if (editor == null) {
5765 editor = getDefaultEditor(getColumnClass(column));
5766 }
5767 return editor;
5768 }
5769
5770
5771 /**
5772 * Prepares the editor by querying the data model for the value and
5773 * selection state of the cell at <code>row</code>, <code>column</code>.
5774 * <p>
5775 * <b>Note:</b>
5776 * Throughout the table package, the internal implementations always
5777 * use this method to prepare editors so that this default behavior
5778 * can be safely overridden by a subclass.
5779 *
5780 * @param editor the <code>TableCellEditor</code> to set up
5781 * @param row the row of the cell to edit,
5782 * where 0 is the first row
5783 * @param column the column of the cell to edit,
5784 * where 0 is the first column
5785 * @return the <code>Component</code> being edited
5786 */
5787 public Component prepareEditor(TableCellEditor editor, int row, int column) {
5788 Object value = getValueAt(row, column);
5789 boolean isSelected = isCellSelected(row, column);
5790 Component comp = editor.getTableCellEditorComponent(this, value, isSelected,
5791 row, column);
5792 if (comp instanceof JComponent) {
5793 JComponent jComp = (JComponent)comp;
5794 if (jComp.getNextFocusableComponent() == null) {
5795 jComp.setNextFocusableComponent(this);
5796 }
5797 }
5798 return comp;
5799 }
5800
5801 /**
5802 * Discards the editor object and frees the real estate it used for
5803 * cell rendering.
5804 */
5805 public void removeEditor() {
5806 KeyboardFocusManager.getCurrentKeyboardFocusManager().
5807 removePropertyChangeListener("permanentFocusOwner", editorRemover);
5808 editorRemover = null;
5809
5810 TableCellEditor editor = getCellEditor();
5811 if(editor != null) {
5812 editor.removeCellEditorListener(this);
5813 if (editorComp != null) {
5814 Component focusOwner =
5815 KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
5816 boolean isFocusOwnerInTheTable = focusOwner != null?
5817 SwingUtilities.isDescendingFrom(focusOwner, this):false;
5818 remove(editorComp);
5819 if(isFocusOwnerInTheTable) {
5820 requestFocusInWindow();
5821 }
5822 }
5823
5824 Rectangle cellRect = getCellRect(editingRow, editingColumn, false);
5825
5826 setCellEditor(null);
5827 setEditingColumn(-1);
5828 setEditingRow(-1);
5829 editorComp = null;
5830
5831 repaint(cellRect);
5832 }
5833 }
5834
5835 //
5836 // Serialization
5837 //
5838
5839 /**
5840 * See readObject() and writeObject() in JComponent for more
5841 * information about serialization in Swing.
5842 */
5843 private void writeObject(ObjectOutputStream s) throws IOException {
5844 s.defaultWriteObject();
5845 if (getUIClassID().equals(uiClassID)) {
5846 byte count = JComponent.getWriteObjCounter(this);
5847 JComponent.setWriteObjCounter(this, --count);
5848 if (count == 0 && ui != null) {
5849 ui.installUI(this);
5850 }
5851 }
5852 }
5853
5854 private void readObject(ObjectInputStream s)
5855 throws IOException, ClassNotFoundException
5856 {
5857 s.defaultReadObject();
5858 if ((ui != null) && (getUIClassID().equals(uiClassID))) {
5859 ui.installUI(this);
5860 }
5861 createDefaultRenderers();
5862 createDefaultEditors();
5863
5864 // If ToolTipText != null, then the tooltip has already been
5865 // registered by JComponent.readObject() and we don't want
5866 // to re-register here
5867 if (getToolTipText() == null) {
5868 ToolTipManager.sharedInstance().registerComponent(this);
5869 }
5870 }
5871
5872 /* Called from the JComponent's EnableSerializationFocusListener to
5873 * do any Swing-specific pre-serialization configuration.
5874 */
5875 void compWriteObjectNotify() {
5876 super.compWriteObjectNotify();
5877 // If ToolTipText != null, then the tooltip has already been
5878 // unregistered by JComponent.compWriteObjectNotify()
5879 if (getToolTipText() == null) {
5880 ToolTipManager.sharedInstance().unregisterComponent(this);
5881 }
5882 }
5883
5884 /**
5885 * Returns a string representation of this table. This method
5886 * is intended to be used only for debugging purposes, and the
5887 * content and format of the returned string may vary between
5888 * implementations. The returned string may be empty but may not
5889 * be <code>null</code>.
5890 *
5891 * @return a string representation of this table
5892 */
5893 protected String paramString() {
5894 String gridColorString = (gridColor != null ?
5895 gridColor.toString() : "");
5896 String showHorizontalLinesString = (showHorizontalLines ?
5897 "true" : "false");
5898 String showVerticalLinesString = (showVerticalLines ?
5899 "true" : "false");
5900 String autoResizeModeString;
5901 if (autoResizeMode == AUTO_RESIZE_OFF) {
5902 autoResizeModeString = "AUTO_RESIZE_OFF";
5903 } else if (autoResizeMode == AUTO_RESIZE_NEXT_COLUMN) {
5904 autoResizeModeString = "AUTO_RESIZE_NEXT_COLUMN";
5905 } else if (autoResizeMode == AUTO_RESIZE_SUBSEQUENT_COLUMNS) {
5906 autoResizeModeString = "AUTO_RESIZE_SUBSEQUENT_COLUMNS";
5907 } else if (autoResizeMode == AUTO_RESIZE_LAST_COLUMN) {
5908 autoResizeModeString = "AUTO_RESIZE_LAST_COLUMN";
5909 } else if (autoResizeMode == AUTO_RESIZE_ALL_COLUMNS) {
5910 autoResizeModeString = "AUTO_RESIZE_ALL_COLUMNS";
5911 } else autoResizeModeString = "";
5912 String autoCreateColumnsFromModelString = (autoCreateColumnsFromModel ?
5913 "true" : "false");
5914 String preferredViewportSizeString = (preferredViewportSize != null ?
5915 preferredViewportSize.toString()
5916 : "");
5917 String rowSelectionAllowedString = (rowSelectionAllowed ?
5918 "true" : "false");
5919 String cellSelectionEnabledString = (cellSelectionEnabled ?
5920 "true" : "false");
5921 String selectionForegroundString = (selectionForeground != null ?
5922 selectionForeground.toString() :
5923 "");
5924 String selectionBackgroundString = (selectionBackground != null ?
5925 selectionBackground.toString() :
5926 "");
5927
5928 return super.paramString() +
5929 ",autoCreateColumnsFromModel=" + autoCreateColumnsFromModelString +
5930 ",autoResizeMode=" + autoResizeModeString +
5931 ",cellSelectionEnabled=" + cellSelectionEnabledString +
5932 ",editingColumn=" + editingColumn +
5933 ",editingRow=" + editingRow +
5934 ",gridColor=" + gridColorString +
5935 ",preferredViewportSize=" + preferredViewportSizeString +
5936 ",rowHeight=" + rowHeight +
5937 ",rowMargin=" + rowMargin +
5938 ",rowSelectionAllowed=" + rowSelectionAllowedString +
5939 ",selectionBackground=" + selectionBackgroundString +
5940 ",selectionForeground=" + selectionForegroundString +
5941 ",showHorizontalLines=" + showHorizontalLinesString +
5942 ",showVerticalLines=" + showVerticalLinesString;
5943 }
5944
5945 // This class tracks changes in the keyboard focus state. It is used
5946 // when the JTable is editing to determine when to cancel the edit.
5947 // If focus switches to a component outside of the jtable, but in the
5948 // same window, this will cancel editing.
5949 class CellEditorRemover implements PropertyChangeListener {
5950 KeyboardFocusManager focusManager;
5951
5952 public CellEditorRemover(KeyboardFocusManager fm) {
5953 this.focusManager = fm;
5954 }
5955
5956 public void propertyChange(PropertyChangeEvent ev) {
5957 if (!isEditing() || getClientProperty("terminateEditOnFocusLost") != Boolean.TRUE) {
5958 return;
5959 }
5960
5961 Component c = focusManager.getPermanentFocusOwner();
5962 while (c != null) {
5963 if (c == JTable.this) {
5964 // focus remains inside the table
5965 return;
5966 } else if ((c instanceof Window) ||
5967 (c instanceof Applet && c.getParent() == null)) {
5968 if (c == SwingUtilities.getRoot(JTable.this)) {
5969 if (!getCellEditor().stopCellEditing()) {
5970 getCellEditor().cancelCellEditing();
5971 }
5972 }
5973 break;
5974 }
5975 c = c.getParent();
5976 }
5977 }
5978 }
5979
5980 /////////////////
5981 // Printing Support
5982 /////////////////
5983
5984 /**
5985 * A convenience method that displays a printing dialog, and then prints
5986 * this <code>JTable</code> in mode <code>PrintMode.FIT_WIDTH</code>,
5987 * with no header or footer text. A modal progress dialog, with an abort
5988 * option, will be shown for the duration of printing.
5989 * <p>
5990 * Note: In headless mode, no dialogs are shown and printing
5991 * occurs on the default printer.
5992 *
5993 * @return true, unless printing is cancelled by the user
5994 * @throws SecurityException if this thread is not allowed to
5995 * initiate a print job request
5996 * @throws PrinterException if an error in the print system causes the job
5997 * to be aborted
5998 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat,
5999 * boolean, PrintRequestAttributeSet, boolean, PrintService)
6000 * @see #getPrintable
6001 *
6002 * @since 1.5
6003 */
6004 public boolean print() throws PrinterException {
6005
6006 return print(PrintMode.FIT_WIDTH);
6007 }
6008
6009 /**
6010 * A convenience method that displays a printing dialog, and then prints
6011 * this <code>JTable</code> in the given printing mode,
6012 * with no header or footer text. A modal progress dialog, with an abort
6013 * option, will be shown for the duration of printing.
6014 * <p>
6015 * Note: In headless mode, no dialogs are shown and printing
6016 * occurs on the default printer.
6017 *
6018 * @param printMode the printing mode that the printable should use
6019 * @return true, unless printing is cancelled by the user
6020 * @throws SecurityException if this thread is not allowed to
6021 * initiate a print job request
6022 * @throws PrinterException if an error in the print system causes the job
6023 * to be aborted
6024 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat,
6025 * boolean, PrintRequestAttributeSet, boolean, PrintService)
6026 * @see #getPrintable
6027 *
6028 * @since 1.5
6029 */
6030 public boolean print(PrintMode printMode) throws PrinterException {
6031
6032 return print(printMode, null, null);
6033 }
6034
6035 /**
6036 * A convenience method that displays a printing dialog, and then prints
6037 * this <code>JTable</code> in the given printing mode,
6038 * with the specified header and footer text. A modal progress dialog,
6039 * with an abort option, will be shown for the duration of printing.
6040 * <p>
6041 * Note: In headless mode, no dialogs are shown and printing
6042 * occurs on the default printer.
6043 *
6044 * @param printMode the printing mode that the printable should use
6045 * @param headerFormat a <code>MessageFormat</code> specifying the text
6046 * to be used in printing a header,
6047 * or null for none
6048 * @param footerFormat a <code>MessageFormat</code> specifying the text
6049 * to be used in printing a footer,
6050 * or null for none
6051 * @return true, unless printing is cancelled by the user
6052 * @throws SecurityException if this thread is not allowed to
6053 * initiate a print job request
6054 * @throws PrinterException if an error in the print system causes the job
6055 * to be aborted
6056 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat,
6057 * boolean, PrintRequestAttributeSet, boolean, PrintService)
6058 * @see #getPrintable
6059 *
6060 * @since 1.5
6061 */
6062 public boolean print(PrintMode printMode,
6063 MessageFormat headerFormat,
6064 MessageFormat footerFormat) throws PrinterException {
6065
6066 boolean showDialogs = !GraphicsEnvironment.isHeadless();
6067 return print(printMode, headerFormat, footerFormat,
6068 showDialogs, null, showDialogs);
6069 }
6070
6071 /**
6072 * Prints this table, as specified by the fully featured
6073 * {@link #print(JTable.PrintMode, MessageFormat, MessageFormat,
6074 * boolean, PrintRequestAttributeSet, boolean, PrintService) print}
6075 * method, with the default printer specified as the print service.
6076 *
6077 * @param printMode the printing mode that the printable should use
6078 * @param headerFormat a <code>MessageFormat</code> specifying the text
6079 * to be used in printing a header,
6080 * or <code>null</code> for none
6081 * @param footerFormat a <code>MessageFormat</code> specifying the text
6082 * to be used in printing a footer,
6083 * or <code>null</code> for none
6084 * @param showPrintDialog whether or not to display a print dialog
6085 * @param attr a <code>PrintRequestAttributeSet</code>
6086 * specifying any printing attributes,
6087 * or <code>null</code> for none
6088 * @param interactive whether or not to print in an interactive mode
6089 * @return true, unless printing is cancelled by the user
6090 * @throws HeadlessException if the method is asked to show a printing
6091 * dialog or run interactively, and
6092 * <code>GraphicsEnvironment.isHeadless</code>
6093 * returns <code>true</code>
6094 * @throws SecurityException if this thread is not allowed to
6095 * initiate a print job request
6096 * @throws PrinterException if an error in the print system causes the job
6097 * to be aborted
6098 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat,
6099 * boolean, PrintRequestAttributeSet, boolean, PrintService)
6100 * @see #getPrintable
6101 *
6102 * @since 1.5
6103 */
6104 public boolean print(PrintMode printMode,
6105 MessageFormat headerFormat,
6106 MessageFormat footerFormat,
6107 boolean showPrintDialog,
6108 PrintRequestAttributeSet attr,
6109 boolean interactive) throws PrinterException,
6110 HeadlessException {
6111
6112 return print(printMode,
6113 headerFormat,
6114 footerFormat,
6115 showPrintDialog,
6116 attr,
6117 interactive,
6118 null);
6119 }
6120
6121 /**
6122 * Prints this <code>JTable</code>. Takes steps that the majority of
6123 * developers would take in order to print a <code>JTable</code>.
6124 * In short, it prepares the table, calls <code>getPrintable</code> to
6125 * fetch an appropriate <code>Printable</code>, and then sends it to the
6126 * printer.
6127 * <p>
6128 * A <code>boolean</code> parameter allows you to specify whether or not
6129 * a printing dialog is displayed to the user. When it is, the user may
6130 * use the dialog to change the destination printer or printing attributes,
6131 * or even to cancel the print. Another two parameters allow for a
6132 * <code>PrintService</code> and printing attributes to be specified.
6133 * These parameters can be used either to provide initial values for the
6134 * print dialog, or to specify values when the dialog is not shown.
6135 * <p>
6136 * A second <code>boolean</code> parameter allows you to specify whether
6137 * or not to perform printing in an interactive mode. If <code>true</code>,
6138 * a modal progress dialog, with an abort option, is displayed for the
6139 * duration of printing . This dialog also prevents any user action which
6140 * may affect the table. However, it can not prevent the table from being
6141 * modified by code (for example, another thread that posts updates using
6142 * <code>SwingUtilities.invokeLater</code>). It is therefore the
6143 * responsibility of the developer to ensure that no other code modifies
6144 * the table in any way during printing (invalid modifications include
6145 * changes in: size, renderers, or underlying data). Printing behavior is
6146 * undefined when the table is changed during printing.
6147 * <p>
6148 * If <code>false</code> is specified for this parameter, no dialog will
6149 * be displayed and printing will begin immediately on the event-dispatch
6150 * thread. This blocks any other events, including repaints, from being
6151 * processed until printing is complete. Although this effectively prevents
6152 * the table from being changed, it doesn't provide a good user experience.
6153 * For this reason, specifying <code>false</code> is only recommended when
6154 * printing from an application with no visible GUI.
6155 * <p>
6156 * Note: Attempting to show the printing dialog or run interactively, while
6157 * in headless mode, will result in a <code>HeadlessException</code>.
6158 * <p>
6159 * Before fetching the printable, this method will gracefully terminate
6160 * editing, if necessary, to prevent an editor from showing in the printed
6161 * result. Additionally, <code>JTable</code> will prepare its renderers
6162 * during printing such that selection and focus are not indicated.
6163 * As far as customizing further how the table looks in the printout,
6164 * developers can provide custom renderers or paint code that conditionalize
6165 * on the value of {@link javax.swing.JComponent#isPaintingForPrint()}.
6166 * <p>
6167 * See {@link #getPrintable} for more description on how the table is
6168 * printed.
6169 *
6170 * @param printMode the printing mode that the printable should use
6171 * @param headerFormat a <code>MessageFormat</code> specifying the text
6172 * to be used in printing a header,
6173 * or <code>null</code> for none
6174 * @param footerFormat a <code>MessageFormat</code> specifying the text
6175 * to be used in printing a footer,
6176 * or <code>null</code> for none
6177 * @param showPrintDialog whether or not to display a print dialog
6178 * @param attr a <code>PrintRequestAttributeSet</code>
6179 * specifying any printing attributes,
6180 * or <code>null</code> for none
6181 * @param interactive whether or not to print in an interactive mode
6182 * @param service the destination <code>PrintService</code>,
6183 * or <code>null</code> to use the default printer
6184 * @return true, unless printing is cancelled by the user
6185 * @throws HeadlessException if the method is asked to show a printing
6186 * dialog or run interactively, and
6187 * <code>GraphicsEnvironment.isHeadless</code>
6188 * returns <code>true</code>
6189 * @throws SecurityException if a security manager exists and its
6190 * {@link java.lang.SecurityManager#checkPrintJobAccess}
6191 * method disallows this thread from creating a print job request
6192 * @throws PrinterException if an error in the print system causes the job
6193 * to be aborted
6194 * @see #getPrintable
6195 * @see java.awt.GraphicsEnvironment#isHeadless
6196 *
6197 * @since 1.6
6198 */
6199 public boolean print(PrintMode printMode,
6200 MessageFormat headerFormat,
6201 MessageFormat footerFormat,
6202 boolean showPrintDialog,
6203 PrintRequestAttributeSet attr,
6204 boolean interactive,
6205 PrintService service) throws PrinterException,
6206 HeadlessException {
6207
6208 // complain early if an invalid parameter is specified for headless mode
6209 boolean isHeadless = GraphicsEnvironment.isHeadless();
6210 if (isHeadless) {
6211 if (showPrintDialog) {
6212 throw new HeadlessException("Can't show print dialog.");
6213 }
6214
6215 if (interactive) {
6216 throw new HeadlessException("Can't run interactively.");
6217 }
6218 }
6219
6220 // Get a PrinterJob.
6221 // Do this before anything with side-effects since it may throw a
6222 // security exception - in which case we don't want to do anything else.
6223 final PrinterJob job = PrinterJob.getPrinterJob();
6224
6225 if (isEditing()) {
6226 // try to stop cell editing, and failing that, cancel it
6227 if (!getCellEditor().stopCellEditing()) {
6228 getCellEditor().cancelCellEditing();
6229 }
6230 }
6231
6232 if (attr == null) {
6233 attr = new HashPrintRequestAttributeSet();
6234 }
6235
6236 final PrintingStatus printingStatus;
6237
6238 // fetch the Printable
6239 Printable printable =
6240 getPrintable(printMode, headerFormat, footerFormat);
6241
6242 if (interactive) {
6243 // wrap the Printable so that we can print on another thread
6244 printable = new ThreadSafePrintable(printable);
6245 printingStatus = PrintingStatus.createPrintingStatus(this, job);
6246 printable = printingStatus.createNotificationPrintable(printable);
6247 } else {
6248 // to please compiler
6249 printingStatus = null;
6250 }
6251
6252 // set the printable on the PrinterJob
6253 job.setPrintable(printable);
6254
6255 // if specified, set the PrintService on the PrinterJob
6256 if (service != null) {
6257 job.setPrintService(service);
6258 }
6259
6260 // if requested, show the print dialog
6261 if (showPrintDialog && !job.printDialog(attr)) {
6262 // the user cancelled the print dialog
6263 return false;
6264 }
6265
6266 // if not interactive, just print on this thread (no dialog)
6267 if (!interactive) {
6268 // do the printing
6269 job.print(attr);
6270
6271 // we're done
6272 return true;
6273 }
6274
6275 // make sure this is clear since we'll check it after
6276 printError = null;
6277
6278 // to synchronize on
6279 final Object lock = new Object();
6280
6281 // copied so we can access from the inner class
6282 final PrintRequestAttributeSet copyAttr = attr;
6283
6284 // this runnable will be used to do the printing
6285 // (and save any throwables) on another thread
6286 Runnable runnable = new Runnable() {
6287 public void run() {
6288 try {
6289 // do the printing
6290 job.print(copyAttr);
6291 } catch (Throwable t) {
6292 // save any Throwable to be rethrown
6293 synchronized(lock) {
6294 printError = t;
6295 }
6296 } finally {
6297 // we're finished - hide the dialog
6298 printingStatus.dispose();
6299 }
6300 }
6301 };
6302
6303 // start printing on another thread
6304 Thread th = new Thread(runnable);
6305 th.start();
6306
6307 printingStatus.showModal(true);
6308
6309 // look for any error that the printing may have generated
6310 Throwable pe;
6311 synchronized(lock) {
6312 pe = printError;
6313 printError = null;
6314 }
6315
6316 // check the type of error and handle it
6317 if (pe != null) {
6318 // a subclass of PrinterException meaning the job was aborted,
6319 // in this case, by the user
6320 if (pe instanceof PrinterAbortException) {
6321 return false;
6322 } else if (pe instanceof PrinterException) {
6323 throw (PrinterException)pe;
6324 } else if (pe instanceof RuntimeException) {
6325 throw (RuntimeException)pe;
6326 } else if (pe instanceof Error) {
6327 throw (Error)pe;
6328 }
6329
6330 // can not happen
6331 throw new AssertionError(pe);
6332 }
6333
6334 return true;
6335 }
6336
6337 /**
6338 * Return a <code>Printable</code> for use in printing this JTable.
6339 * <p>
6340 * This method is meant for those wishing to customize the default
6341 * <code>Printable</code> implementation used by <code>JTable</code>'s
6342 * <code>print</code> methods. Developers wanting simply to print the table
6343 * should use one of those methods directly.
6344 * <p>
6345 * The <code>Printable</code> can be requested in one of two printing modes.
6346 * In both modes, it spreads table rows naturally in sequence across
6347 * multiple pages, fitting as many rows as possible per page.
6348 * <code>PrintMode.NORMAL</code> specifies that the table be
6349 * printed at its current size. In this mode, there may be a need to spread
6350 * columns across pages in a similar manner to that of the rows. When the
6351 * need arises, columns are distributed in an order consistent with the
6352 * table's <code>ComponentOrientation</code>.
6353 * <code>PrintMode.FIT_WIDTH</code> specifies that the output be
6354 * scaled smaller, if necessary, to fit the table's entire width
6355 * (and thereby all columns) on each page. Width and height are scaled
6356 * equally, maintaining the aspect ratio of the output.
6357 * <p>
6358 * The <code>Printable</code> heads the portion of table on each page
6359 * with the appropriate section from the table's <code>JTableHeader</code>,
6360 * if it has one.
6361 * <p>
6362 * Header and footer text can be added to the output by providing
6363 * <code>MessageFormat</code> arguments. The printing code requests
6364 * Strings from the formats, providing a single item which may be included
6365 * in the formatted string: an <code>Integer</code> representing the current
6366 * page number.
6367 * <p>
6368 * You are encouraged to read the documentation for
6369 * <code>MessageFormat</code> as some characters, such as single-quote,
6370 * are special and need to be escaped.
6371 * <p>
6372 * Here's an example of creating a <code>MessageFormat</code> that can be
6373 * used to print "Duke's Table: Page - " and the current page number:
6374 * <p>
6375 * <pre>
6376 * // notice the escaping of the single quote
6377 * // notice how the page number is included with "{0}"
6378 * MessageFormat format = new MessageFormat("Duke''s Table: Page - {0}");
6379 * </pre>
6380 * <p>
6381 * The <code>Printable</code> constrains what it draws to the printable
6382 * area of each page that it prints. Under certain circumstances, it may
6383 * find it impossible to fit all of a page's content into that area. In
6384 * these cases the output may be clipped, but the implementation
6385 * makes an effort to do something reasonable. Here are a few situations
6386 * where this is known to occur, and how they may be handled by this
6387 * particular implementation:
6388 * <ul>
6389 * <li>In any mode, when the header or footer text is too wide to fit
6390 * completely in the printable area -- print as much of the text as
6391 * possible starting from the beginning, as determined by the table's
6392 * <code>ComponentOrientation</code>.
6393 * <li>In any mode, when a row is too tall to fit in the
6394 * printable area -- print the upper-most portion of the row
6395 * and paint no lower border on the table.
6396 * <li>In <code>PrintMode.NORMAL</code> when a column
6397 * is too wide to fit in the printable area -- print the center
6398 * portion of the column and leave the left and right borders
6399 * off the table.
6400 * </ul>
6401 * <p>
6402 * It is entirely valid for this <code>Printable</code> to be wrapped
6403 * inside another in order to create complex reports and documents. You may
6404 * even request that different pages be rendered into different sized
6405 * printable areas. The implementation must be prepared to handle this
6406 * (possibly by doing its layout calculations on the fly). However,
6407 * providing different heights to each page will likely not work well
6408 * with <code>PrintMode.NORMAL</code> when it has to spread columns
6409 * across pages.
6410 * <p>
6411 * As far as customizing how the table looks in the printed result,
6412 * <code>JTable</code> itself will take care of hiding the selection
6413 * and focus during printing. For additional customizations, your
6414 * renderers or painting code can customize the look based on the value
6415 * of {@link javax.swing.JComponent#isPaintingForPrint()}
6416 * <p>
6417 * Also, <i>before</i> calling this method you may wish to <i>first</i>
6418 * modify the state of the table, such as to cancel cell editing or
6419 * have the user size the table appropriately. However, you must not
6420 * modify the state of the table <i>after</i> this <code>Printable</code>
6421 * has been fetched (invalid modifications include changes in size or
6422 * underlying data). The behavior of the returned <code>Printable</code>
6423 * is undefined once the table has been changed.
6424 *
6425 * @param printMode the printing mode that the printable should use
6426 * @param headerFormat a <code>MessageFormat</code> specifying the text to
6427 * be used in printing a header, or null for none
6428 * @param footerFormat a <code>MessageFormat</code> specifying the text to
6429 * be used in printing a footer, or null for none
6430 * @return a <code>Printable</code> for printing this JTable
6431 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat,
6432 * boolean, PrintRequestAttributeSet, boolean)
6433 * @see Printable
6434 * @see PrinterJob
6435 *
6436 * @since 1.5
6437 */
6438 public Printable getPrintable(PrintMode printMode,
6439 MessageFormat headerFormat,
6440 MessageFormat footerFormat) {
6441
6442 return new TablePrintable(this, printMode, headerFormat, footerFormat);
6443 }
6444
6445
6446 /**
6447 * A <code>Printable</code> implementation that wraps another
6448 * <code>Printable</code>, making it safe for printing on another thread.
6449 */
6450 private class ThreadSafePrintable implements Printable {
6451
6452 /** The delegate <code>Printable</code>. */
6453 private Printable printDelegate;
6454
6455 /**
6456 * To communicate any return value when delegating.
6457 */
6458 private int retVal;
6459
6460 /**
6461 * To communicate any <code>Throwable</code> when delegating.
6462 */
6463 private Throwable retThrowable;
6464
6465 /**
6466 * Construct a <code>ThreadSafePrintable</code> around the given
6467 * delegate.
6468 *
6469 * @param printDelegate the <code>Printable</code> to delegate to
6470 */
6471 public ThreadSafePrintable(Printable printDelegate) {
6472 this.printDelegate = printDelegate;
6473 }
6474
6475 /**
6476 * Prints the specified page into the given {@link Graphics}
6477 * context, in the specified format.
6478 * <p>
6479 * Regardless of what thread this method is called on, all calls into
6480 * the delegate will be done on the event-dispatch thread.
6481 *
6482 * @param graphics the context into which the page is drawn
6483 * @param pageFormat the size and orientation of the page being drawn
6484 * @param pageIndex the zero based index of the page to be drawn
6485 * @return PAGE_EXISTS if the page is rendered successfully, or
6486 * NO_SUCH_PAGE if a non-existent page index is specified
6487 * @throws PrinterException if an error causes printing to be aborted
6488 */
6489 public int print(final Graphics graphics,
6490 final PageFormat pageFormat,
6491 final int pageIndex) throws PrinterException {
6492
6493 // We'll use this Runnable
6494 Runnable runnable = new Runnable() {
6495 public synchronized void run() {
6496 try {
6497 // call into the delegate and save the return value
6498 retVal = printDelegate.print(graphics, pageFormat, pageIndex);
6499 } catch (Throwable throwable) {
6500 // save any Throwable to be rethrown
6501 retThrowable = throwable;
6502 } finally {
6503 // notify the caller that we're done
6504 notifyAll();
6505 }
6506 }
6507 };
6508
6509 synchronized(runnable) {
6510 // make sure these are initialized
6511 retVal = -1;
6512 retThrowable = null;
6513
6514 // call into the EDT
6515 SwingUtilities.invokeLater(runnable);
6516
6517 // wait for the runnable to finish
6518 while (retVal == -1 && retThrowable == null) {
6519 try {
6520 runnable.wait();
6521 } catch (InterruptedException ie) {
6522 // short process, safe to ignore interrupts
6523 }
6524 }
6525
6526 // if the delegate threw a throwable, rethrow it here
6527 if (retThrowable != null) {
6528 if (retThrowable instanceof PrinterException) {
6529 throw (PrinterException)retThrowable;
6530 } else if (retThrowable instanceof RuntimeException) {
6531 throw (RuntimeException)retThrowable;
6532 } else if (retThrowable instanceof Error) {
6533 throw (Error)retThrowable;
6534 }
6535
6536 // can not happen
6537 throw new AssertionError(retThrowable);
6538 }
6539
6540 return retVal;
6541 }
6542 }
6543 }
6544
6545
6546 /////////////////
6547 // Accessibility support
6548 ////////////////
6549
6550 /**
6551 * Gets the AccessibleContext associated with this JTable.
6552 * For tables, the AccessibleContext takes the form of an
6553 * AccessibleJTable.
6554 * A new AccessibleJTable instance is created if necessary.
6555 *
6556 * @return an AccessibleJTable that serves as the
6557 * AccessibleContext of this JTable
6558 */
6559 public AccessibleContext getAccessibleContext() {
6560 if (accessibleContext == null) {
6561 accessibleContext = new AccessibleJTable();
6562 }
6563 return accessibleContext;
6564 }
6565
6566 //
6567 // *** should also implement AccessibleSelection?
6568 // *** and what's up with keyboard navigation/manipulation?
6569 //
6570 /**
6571 * This class implements accessibility support for the
6572 * <code>JTable</code> class. It provides an implementation of the
6573 * Java Accessibility API appropriate to table user-interface elements.
6574 * <p>
6575 * <strong>Warning:</strong>
6576 * Serialized objects of this class will not be compatible with
6577 * future Swing releases. The current serialization support is
6578 * appropriate for short term storage or RMI between applications running
6579 * the same version of Swing. As of 1.4, support for long term storage
6580 * of all JavaBeans<sup><font size="-2">TM</font></sup>
6581 * has been added to the <code>java.beans</code> package.
6582 * Please see {@link java.beans.XMLEncoder}.
6583 */
6584 protected class AccessibleJTable extends AccessibleJComponent
6585 implements AccessibleSelection, ListSelectionListener, TableModelListener,
6586 TableColumnModelListener, CellEditorListener, PropertyChangeListener,
6587 AccessibleExtendedTable {
6588
6589 int lastSelectedRow;
6590 int lastSelectedCol;
6591
6592 /**
6593 * AccessibleJTable constructor
6594 *
6595 * @since 1.5
6596 */
6597 protected AccessibleJTable() {
6598 super();
6599 JTable.this.addPropertyChangeListener(this);
6600 JTable.this.getSelectionModel().addListSelectionListener(this);
6601 TableColumnModel tcm = JTable.this.getColumnModel();
6602 tcm.addColumnModelListener(this);
6603 tcm.getSelectionModel().addListSelectionListener(this);
6604 JTable.this.getModel().addTableModelListener(this);
6605 lastSelectedRow = JTable.this.getSelectedRow();
6606 lastSelectedCol = JTable.this.getSelectedColumn();
6607 }
6608
6609 // Listeners to track model, etc. changes to as to re-place the other
6610 // listeners
6611
6612 /**
6613 * Track changes to selection model, column model, etc. so as to
6614 * be able to re-place listeners on those in order to pass on
6615 * information to the Accessibility PropertyChange mechanism
6616 */
6617 public void propertyChange(PropertyChangeEvent e) {
6618 String name = e.getPropertyName();
6619 Object oldValue = e.getOldValue();
6620 Object newValue = e.getNewValue();
6621
6622 // re-set tableModel listeners
6623 if (name.compareTo("model") == 0) {
6624
6625 if (oldValue != null && oldValue instanceof TableModel) {
6626 ((TableModel) oldValue).removeTableModelListener(this);
6627 }
6628 if (newValue != null && newValue instanceof TableModel) {
6629 ((TableModel) newValue).addTableModelListener(this);
6630 }
6631
6632 // re-set selectionModel listeners
6633 } else if (name.compareTo("selectionModel") == 0) {
6634
6635 Object source = e.getSource();
6636 if (source == JTable.this) { // row selection model
6637
6638 if (oldValue != null &&
6639 oldValue instanceof ListSelectionModel) {
6640 ((ListSelectionModel) oldValue).removeListSelectionListener(this);
6641 }
6642 if (newValue != null &&
6643 newValue instanceof ListSelectionModel) {
6644 ((ListSelectionModel) newValue).addListSelectionListener(this);
6645 }
6646
6647 } else if (source == JTable.this.getColumnModel()) {
6648
6649 if (oldValue != null &&
6650 oldValue instanceof ListSelectionModel) {
6651 ((ListSelectionModel) oldValue).removeListSelectionListener(this);
6652 }
6653 if (newValue != null &&
6654 newValue instanceof ListSelectionModel) {
6655 ((ListSelectionModel) newValue).addListSelectionListener(this);
6656 }
6657
6658 } else {
6659 // System.out.println("!!! Bug in source of selectionModel propertyChangeEvent");
6660 }
6661
6662 // re-set columnModel listeners
6663 // and column's selection property listener as well
6664 } else if (name.compareTo("columnModel") == 0) {
6665
6666 if (oldValue != null && oldValue instanceof TableColumnModel) {
6667 TableColumnModel tcm = (TableColumnModel) oldValue;
6668 tcm.removeColumnModelListener(this);
6669 tcm.getSelectionModel().removeListSelectionListener(this);
6670 }
6671 if (newValue != null && newValue instanceof TableColumnModel) {
6672 TableColumnModel tcm = (TableColumnModel) newValue;
6673 tcm.addColumnModelListener(this);
6674 tcm.getSelectionModel().addListSelectionListener(this);
6675 }
6676
6677 // re-se cellEditor listeners
6678 } else if (name.compareTo("tableCellEditor") == 0) {
6679
6680 if (oldValue != null && oldValue instanceof TableCellEditor) {
6681 ((TableCellEditor) oldValue).removeCellEditorListener(this);
6682 }
6683 if (newValue != null && newValue instanceof TableCellEditor) {
6684 ((TableCellEditor) newValue).addCellEditorListener(this);
6685 }
6686 }
6687 }
6688
6689
6690 // Listeners to echo changes to the AccessiblePropertyChange mechanism
6691
6692 /*
6693 * Describes a change in the accessible table model.
6694 */
6695 protected class AccessibleJTableModelChange
6696 implements AccessibleTableModelChange {
6697
6698 protected int type;
6699 protected int firstRow;
6700 protected int lastRow;
6701 protected int firstColumn;
6702 protected int lastColumn;
6703
6704 protected AccessibleJTableModelChange(int type, int firstRow,
6705 int lastRow, int firstColumn,
6706 int lastColumn) {
6707 this.type = type;
6708 this.firstRow = firstRow;
6709 this.lastRow = lastRow;
6710 this.firstColumn = firstColumn;
6711 this.lastColumn = lastColumn;
6712 }
6713
6714 public int getType() {
6715 return type;
6716 }
6717
6718 public int getFirstRow() {
6719 return firstRow;
6720 }
6721
6722 public int getLastRow() {
6723 return lastRow;
6724 }
6725
6726 public int getFirstColumn() {
6727 return firstColumn;
6728 }
6729
6730 public int getLastColumn() {
6731 return lastColumn;
6732 }
6733 }
6734
6735 /**
6736 * Track changes to the table contents
6737 */
6738 public void tableChanged(TableModelEvent e) {
6739 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
6740 null, null);
6741 if (e != null) {
6742 int firstColumn = e.getColumn();
6743 int lastColumn = e.getColumn();
6744 if (firstColumn == TableModelEvent.ALL_COLUMNS) {
6745 firstColumn = 0;
6746 lastColumn = getColumnCount() - 1;
6747 }
6748
6749 // Fire a property change event indicating the table model
6750 // has changed.
6751 AccessibleJTableModelChange change =
6752 new AccessibleJTableModelChange(e.getType(),
6753 e.getFirstRow(),
6754 e.getLastRow(),
6755 firstColumn,
6756 lastColumn);
6757 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
6758 null, change);
6759 }
6760 }
6761
6762 /**
6763 * Track changes to the table contents (row insertions)
6764 */
6765 public void tableRowsInserted(TableModelEvent e) {
6766 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
6767 null, null);
6768
6769 // Fire a property change event indicating the table model
6770 // has changed.
6771 int firstColumn = e.getColumn();
6772 int lastColumn = e.getColumn();
6773 if (firstColumn == TableModelEvent.ALL_COLUMNS) {
6774 firstColumn = 0;
6775 lastColumn = getColumnCount() - 1;
6776 }
6777 AccessibleJTableModelChange change =
6778 new AccessibleJTableModelChange(e.getType(),
6779 e.getFirstRow(),
6780 e.getLastRow(),
6781 firstColumn,
6782 lastColumn);
6783 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
6784 null, change);
6785 }
6786
6787 /**
6788 * Track changes to the table contents (row deletions)
6789 */
6790 public void tableRowsDeleted(TableModelEvent e) {
6791 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
6792 null, null);
6793
6794 // Fire a property change event indicating the table model
6795 // has changed.
6796 int firstColumn = e.getColumn();
6797 int lastColumn = e.getColumn();
6798 if (firstColumn == TableModelEvent.ALL_COLUMNS) {
6799 firstColumn = 0;
6800 lastColumn = getColumnCount() - 1;
6801 }
6802 AccessibleJTableModelChange change =
6803 new AccessibleJTableModelChange(e.getType(),
6804 e.getFirstRow(),
6805 e.getLastRow(),
6806 firstColumn,
6807 lastColumn);
6808 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
6809 null, change);
6810 }
6811
6812 /**
6813 * Track changes to the table contents (column insertions)
6814 */
6815 public void columnAdded(TableColumnModelEvent e) {
6816 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
6817 null, null);
6818
6819 // Fire a property change event indicating the table model
6820 // has changed.
6821 int type = AccessibleTableModelChange.INSERT;
6822 AccessibleJTableModelChange change =
6823 new AccessibleJTableModelChange(type,
6824 0,
6825 0,
6826 e.getFromIndex(),
6827 e.getToIndex());
6828 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
6829 null, change);
6830 }
6831
6832 /**
6833 * Track changes to the table contents (column deletions)
6834 */
6835 public void columnRemoved(TableColumnModelEvent e) {
6836 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
6837 null, null);
6838 // Fire a property change event indicating the table model
6839 // has changed.
6840 int type = AccessibleTableModelChange.DELETE;
6841 AccessibleJTableModelChange change =
6842 new AccessibleJTableModelChange(type,
6843 0,
6844 0,
6845 e.getFromIndex(),
6846 e.getToIndex());
6847 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
6848 null, change);
6849 }
6850
6851 /**
6852 * Track changes of a column repositioning.
6853 *
6854 * @see TableColumnModelListener
6855 */
6856 public void columnMoved(TableColumnModelEvent e) {
6857 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
6858 null, null);
6859
6860 // Fire property change events indicating the table model
6861 // has changed.
6862 int type = AccessibleTableModelChange.DELETE;
6863 AccessibleJTableModelChange change =
6864 new AccessibleJTableModelChange(type,
6865 0,
6866 0,
6867 e.getFromIndex(),
6868 e.getFromIndex());
6869 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
6870 null, change);
6871
6872 int type2 = AccessibleTableModelChange.INSERT;
6873 AccessibleJTableModelChange change2 =
6874 new AccessibleJTableModelChange(type2,
6875 0,
6876 0,
6877 e.getToIndex(),
6878 e.getToIndex());
6879 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
6880 null, change2);
6881 }
6882
6883 /**
6884 * Track changes of a column moving due to margin changes.
6885 *
6886 * @see TableColumnModelListener
6887 */
6888 public void columnMarginChanged(ChangeEvent e) {
6889 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
6890 null, null);
6891 }
6892
6893 /**
6894 * Track that the selection model of the TableColumnModel changed.
6895 *
6896 * @see TableColumnModelListener
6897 */
6898 public void columnSelectionChanged(ListSelectionEvent e) {
6899 // we should now re-place our TableColumn listener
6900 }
6901
6902 /**
6903 * Track changes to a cell's contents.
6904 *
6905 * Invoked when editing is finished. The changes are saved, the
6906 * editor object is discarded, and the cell is rendered once again.
6907 *
6908 * @see CellEditorListener
6909 */
6910 public void editingStopped(ChangeEvent e) {
6911 // it'd be great if we could figure out which cell, and pass that
6912 // somehow as a parameter
6913 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
6914 null, null);
6915 }
6916
6917 /**
6918 * Invoked when editing is canceled. The editor object is discarded
6919 * and the cell is rendered once again.
6920 *
6921 * @see CellEditorListener
6922 */
6923 public void editingCanceled(ChangeEvent e) {
6924 // nothing to report, 'cause nothing changed
6925 }
6926
6927 /**
6928 * Track changes to table cell selections
6929 */
6930 public void valueChanged(ListSelectionEvent e) {
6931 firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
6932 Boolean.valueOf(false), Boolean.valueOf(true));
6933
6934 int selectedRow = JTable.this.getSelectedRow();
6935 int selectedCol = JTable.this.getSelectedColumn();
6936 if (selectedRow != lastSelectedRow ||
6937 selectedCol != lastSelectedCol) {
6938 Accessible oldA = getAccessibleAt(lastSelectedRow,
6939 lastSelectedCol);
6940 Accessible newA = getAccessibleAt(selectedRow, selectedCol);
6941 firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
6942 oldA, newA);
6943 lastSelectedRow = selectedRow;
6944 lastSelectedCol = selectedCol;
6945 }
6946 }
6947
6948
6949
6950
6951 // AccessibleContext support
6952
6953 /**
6954 * Get the AccessibleSelection associated with this object. In the
6955 * implementation of the Java Accessibility API for this class,
6956 * return this object, which is responsible for implementing the
6957 * AccessibleSelection interface on behalf of itself.
6958 *
6959 * @return this object
6960 */
6961 public AccessibleSelection getAccessibleSelection() {
6962 return this;
6963 }
6964
6965 /**
6966 * Gets the role of this object.
6967 *
6968 * @return an instance of AccessibleRole describing the role of the
6969 * object
6970 * @see AccessibleRole
6971 */
6972 public AccessibleRole getAccessibleRole() {
6973 return AccessibleRole.TABLE;
6974 }
6975
6976 /**
6977 * Returns the <code>Accessible</code> child, if one exists,
6978 * contained at the local coordinate <code>Point</code>.
6979 *
6980 * @param p the point defining the top-left corner of the
6981 * <code>Accessible</code>, given in the coordinate space
6982 * of the object's parent
6983 * @return the <code>Accessible</code>, if it exists,
6984 * at the specified location; else <code>null</code>
6985 */
6986 public Accessible getAccessibleAt(Point p) {
6987 int column = columnAtPoint(p);
6988 int row = rowAtPoint(p);
6989
6990 if ((column != -1) && (row != -1)) {
6991 TableColumn aColumn = getColumnModel().getColumn(column);
6992 TableCellRenderer renderer = aColumn.getCellRenderer();
6993 if (renderer == null) {
6994 Class<?> columnClass = getColumnClass(column);
6995 renderer = getDefaultRenderer(columnClass);
6996 }
6997 Component component = renderer.getTableCellRendererComponent(
6998 JTable.this, null, false, false,
6999 row, column);
7000 return new AccessibleJTableCell(JTable.this, row, column,
7001 getAccessibleIndexAt(row, column));
7002 }
7003 return null;
7004 }
7005
7006 /**
7007 * Returns the number of accessible children in the object. If all
7008 * of the children of this object implement <code>Accessible</code>,
7009 * then this method should return the number of children of this object.
7010 *
7011 * @return the number of accessible children in the object
7012 */
7013 public int getAccessibleChildrenCount() {
7014 return (JTable.this.getColumnCount() * JTable.this.getRowCount());
7015 }
7016
7017 /**
7018 * Returns the nth <code>Accessible</code> child of the object.
7019 *
7020 * @param i zero-based index of child
7021 * @return the nth Accessible child of the object
7022 */
7023 public Accessible getAccessibleChild(int i) {
7024 if (i < 0 || i >= getAccessibleChildrenCount()) {
7025 return null;
7026 } else {
7027 // children increase across, and then down, for tables
7028 // (arbitrary decision)
7029 int column = getAccessibleColumnAtIndex(i);
7030 int row = getAccessibleRowAtIndex(i);
7031
7032 TableColumn aColumn = getColumnModel().getColumn(column);
7033 TableCellRenderer renderer = aColumn.getCellRenderer();
7034 if (renderer == null) {
7035 Class<?> columnClass = getColumnClass(column);
7036 renderer = getDefaultRenderer(columnClass);
7037 }
7038 Component component = renderer.getTableCellRendererComponent(
7039 JTable.this, null, false, false,
7040 row, column);
7041 return new AccessibleJTableCell(JTable.this, row, column,
7042 getAccessibleIndexAt(row, column));
7043 }
7044 }
7045
7046 // AccessibleSelection support
7047
7048 /**
7049 * Returns the number of <code>Accessible</code> children
7050 * currently selected.
7051 * If no children are selected, the return value will be 0.
7052 *
7053 * @return the number of items currently selected
7054 */
7055 public int getAccessibleSelectionCount() {
7056 int rowsSel = JTable.this.getSelectedRowCount();
7057 int colsSel = JTable.this.getSelectedColumnCount();
7058
7059 if (JTable.this.cellSelectionEnabled) { // a contiguous block
7060 return rowsSel * colsSel;
7061
7062 } else {
7063 // a column swath and a row swath, with a shared block
7064 if (JTable.this.getRowSelectionAllowed() &&
7065 JTable.this.getColumnSelectionAllowed()) {
7066 return rowsSel * JTable.this.getColumnCount() +
7067 colsSel * JTable.this.getRowCount() -
7068 rowsSel * colsSel;
7069
7070 // just one or more rows in selection
7071 } else if (JTable.this.getRowSelectionAllowed()) {
7072 return rowsSel * JTable.this.getColumnCount();
7073
7074 // just one or more rows in selection
7075 } else if (JTable.this.getColumnSelectionAllowed()) {
7076 return colsSel * JTable.this.getRowCount();
7077
7078 } else {
7079 return 0; // JTable doesn't allow selections
7080 }
7081 }
7082 }
7083
7084 /**
7085 * Returns an <code>Accessible</code> representing the
7086 * specified selected child in the object. If there
7087 * isn't a selection, or there are fewer children selected
7088 * than the integer passed in, the return
7089 * value will be <code>null</code>.
7090 * <p>Note that the index represents the i-th selected child, which
7091 * is different from the i-th child.
7092 *
7093 * @param i the zero-based index of selected children
7094 * @return the i-th selected child
7095 * @see #getAccessibleSelectionCount
7096 */
7097 public Accessible getAccessibleSelection(int i) {
7098 if (i < 0 || i > getAccessibleSelectionCount()) {
7099 return null;
7100 }
7101
7102 int rowsSel = JTable.this.getSelectedRowCount();
7103 int colsSel = JTable.this.getSelectedColumnCount();
7104 int rowIndicies[] = getSelectedRows();
7105 int colIndicies[] = getSelectedColumns();
7106 int ttlCols = JTable.this.getColumnCount();
7107 int ttlRows = JTable.this.getRowCount();
7108 int r;
7109 int c;
7110
7111 if (JTable.this.cellSelectionEnabled) { // a contiguous block
7112 r = rowIndicies[i / colsSel];
7113 c = colIndicies[i % colsSel];
7114 return getAccessibleChild((r * ttlCols) + c);
7115 } else {
7116
7117 // a column swath and a row swath, with a shared block
7118 if (JTable.this.getRowSelectionAllowed() &&
7119 JTable.this.getColumnSelectionAllowed()) {
7120
7121 // Situation:
7122 // We have a table, like the 6x3 table below,
7123 // wherein three colums and one row selected
7124 // (selected cells marked with "*", unselected "0"):
7125 //
7126 // 0 * 0 * * 0
7127 // * * * * * *
7128 // 0 * 0 * * 0
7129 //
7130
7131 // State machine below walks through the array of
7132 // selected rows in two states: in a selected row,
7133 // and not in one; continuing until we are in a row
7134 // in which the ith selection exists. Then we return
7135 // the appropriate cell. In the state machine, we
7136 // always do rows above the "current" selected row first,
7137 // then the cells in the selected row. If we're done
7138 // with the state machine before finding the requested
7139 // selected child, we handle the rows below the last
7140 // selected row at the end.
7141 //
7142 int curIndex = i;
7143 final int IN_ROW = 0;
7144 final int NOT_IN_ROW = 1;
7145 int state = (rowIndicies[0] == 0 ? IN_ROW : NOT_IN_ROW);
7146 int j = 0;
7147 int prevRow = -1;
7148 while (j < rowIndicies.length) {
7149 switch (state) {
7150
7151 case IN_ROW: // on individual row full of selections
7152 if (curIndex < ttlCols) { // it's here!
7153 c = curIndex % ttlCols;
7154 r = rowIndicies[j];
7155 return getAccessibleChild((r * ttlCols) + c);
7156 } else { // not here
7157 curIndex -= ttlCols;
7158 }
7159 // is the next row in table selected or not?
7160 if (j + 1 == rowIndicies.length ||
7161 rowIndicies[j] != rowIndicies[j+1] - 1) {
7162 state = NOT_IN_ROW;
7163 prevRow = rowIndicies[j];
7164 }
7165 j++; // we didn't return earlier, so go to next row
7166 break;
7167
7168 case NOT_IN_ROW: // sparse bunch of rows of selections
7169 if (curIndex <
7170 (colsSel * (rowIndicies[j] -
7171 (prevRow == -1 ? 0 : (prevRow + 1))))) {
7172
7173 // it's here!
7174 c = colIndicies[curIndex % colsSel];
7175 r = (j > 0 ? rowIndicies[j-1] + 1 : 0)
7176 + curIndex / colsSel;
7177 return getAccessibleChild((r * ttlCols) + c);
7178 } else { // not here
7179 curIndex -= colsSel * (rowIndicies[j] -
7180 (prevRow == -1 ? 0 : (prevRow + 1)));
7181 }
7182 state = IN_ROW;
7183 break;
7184 }
7185 }
7186 // we got here, so we didn't find it yet; find it in
7187 // the last sparse bunch of rows
7188 if (curIndex <
7189 (colsSel * (ttlRows -
7190 (prevRow == -1 ? 0 : (prevRow + 1))))) { // it's here!
7191 c = colIndicies[curIndex % colsSel];
7192 r = rowIndicies[j-1] + curIndex / colsSel + 1;
7193 return getAccessibleChild((r * ttlCols) + c);
7194 } else { // not here
7195 // we shouldn't get to this spot in the code!
7196 // System.out.println("Bug in AccessibleJTable.getAccessibleSelection()");
7197 }
7198
7199 // one or more rows selected
7200 } else if (JTable.this.getRowSelectionAllowed()) {
7201 c = i % ttlCols;
7202 r = rowIndicies[i / ttlCols];
7203 return getAccessibleChild((r * ttlCols) + c);
7204
7205 // one or more columns selected
7206 } else if (JTable.this.getColumnSelectionAllowed()) {
7207 c = colIndicies[i % colsSel];
7208 r = i / colsSel;
7209 return getAccessibleChild((r * ttlCols) + c);
7210 }
7211 }
7212 return null;
7213 }
7214
7215 /**
7216 * Determines if the current child of this object is selected.
7217 *
7218 * @param i the zero-based index of the child in this
7219 * <code>Accessible</code> object
7220 * @return true if the current child of this object is selected
7221 * @see AccessibleContext#getAccessibleChild
7222 */
7223 public boolean isAccessibleChildSelected(int i) {
7224 int column = getAccessibleColumnAtIndex(i);
7225 int row = getAccessibleRowAtIndex(i);
7226 return JTable.this.isCellSelected(row, column);
7227 }
7228
7229 /**
7230 * Adds the specified <code>Accessible</code> child of the
7231 * object to the object's selection. If the object supports
7232 * multiple selections, the specified child is added to
7233 * any existing selection, otherwise
7234 * it replaces any existing selection in the object. If the
7235 * specified child is already selected, this method has no effect.
7236 * <p>
7237 * This method only works on <code>JTable</code>s which have
7238 * individual cell selection enabled.
7239 *
7240 * @param i the zero-based index of the child
7241 * @see AccessibleContext#getAccessibleChild
7242 */
7243 public void addAccessibleSelection(int i) {
7244 // TIGER - 4495286
7245 int column = getAccessibleColumnAtIndex(i);
7246 int row = getAccessibleRowAtIndex(i);
7247 JTable.this.changeSelection(row, column, true, false);
7248 }
7249
7250 /**
7251 * Removes the specified child of the object from the object's
7252 * selection. If the specified item isn't currently selected, this
7253 * method has no effect.
7254 * <p>
7255 * This method only works on <code>JTables</code> which have
7256 * individual cell selection enabled.
7257 *
7258 * @param i the zero-based index of the child
7259 * @see AccessibleContext#getAccessibleChild
7260 */
7261 public void removeAccessibleSelection(int i) {
7262 if (JTable.this.cellSelectionEnabled) {
7263 int column = getAccessibleColumnAtIndex(i);
7264 int row = getAccessibleRowAtIndex(i);
7265 JTable.this.removeRowSelectionInterval(row, row);
7266 JTable.this.removeColumnSelectionInterval(column, column);
7267 }
7268 }
7269
7270 /**
7271 * Clears the selection in the object, so that no children in the
7272 * object are selected.
7273 */
7274 public void clearAccessibleSelection() {
7275 JTable.this.clearSelection();
7276 }
7277
7278 /**
7279 * Causes every child of the object to be selected, but only
7280 * if the <code>JTable</code> supports multiple selections,
7281 * and if individual cell selection is enabled.
7282 */
7283 public void selectAllAccessibleSelection() {
7284 if (JTable.this.cellSelectionEnabled) {
7285 JTable.this.selectAll();
7286 }
7287 }
7288
7289 // begin AccessibleExtendedTable implementation -------------
7290
7291 /**
7292 * Returns the row number of an index in the table.
7293 *
7294 * @param index the zero-based index in the table
7295 * @return the zero-based row of the table if one exists;
7296 * otherwise -1.
7297 * @since 1.4
7298 */
7299 public int getAccessibleRow(int index) {
7300 return getAccessibleRowAtIndex(index);
7301 }
7302
7303 /**
7304 * Returns the column number of an index in the table.
7305 *
7306 * @param index the zero-based index in the table
7307 * @return the zero-based column of the table if one exists;
7308 * otherwise -1.
7309 * @since 1.4
7310 */
7311 public int getAccessibleColumn(int index) {
7312 return getAccessibleColumnAtIndex(index);
7313 }
7314
7315 /**
7316 * Returns the index at a row and column in the table.
7317 *
7318 * @param r zero-based row of the table
7319 * @param c zero-based column of the table
7320 * @return the zero-based index in the table if one exists;
7321 * otherwise -1.
7322 * @since 1.4
7323 */
7324 public int getAccessibleIndex(int r, int c) {
7325 return getAccessibleIndexAt(r, c);
7326 }
7327
7328 // end of AccessibleExtendedTable implementation ------------
7329
7330 // start of AccessibleTable implementation ------------------
7331
7332 private Accessible caption;
7333 private Accessible summary;
7334 private Accessible [] rowDescription;
7335 private Accessible [] columnDescription;
7336
7337 /**
7338 * Gets the <code>AccessibleTable</code> associated with this
7339 * object. In the implementation of the Java Accessibility
7340 * API for this class, return this object, which is responsible
7341 * for implementing the <code>AccessibleTables</code> interface
7342 * on behalf of itself.
7343 *
7344 * @return this object
7345 * @since 1.3
7346 */
7347 public AccessibleTable getAccessibleTable() {
7348 return this;
7349 }
7350
7351 /**
7352 * Returns the caption for the table.
7353 *
7354 * @return the caption for the table
7355 * @since 1.3
7356 */
7357 public Accessible getAccessibleCaption() {
7358 return this.caption;
7359 }
7360
7361 /**
7362 * Sets the caption for the table.
7363 *
7364 * @param a the caption for the table
7365 * @since 1.3
7366 */
7367 public void setAccessibleCaption(Accessible a) {
7368 Accessible oldCaption = caption;
7369 this.caption = a;
7370 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_CAPTION_CHANGED,
7371 oldCaption, this.caption);
7372 }
7373
7374 /**
7375 * Returns the summary description of the table.
7376 *
7377 * @return the summary description of the table
7378 * @since 1.3
7379 */
7380 public Accessible getAccessibleSummary() {
7381 return this.summary;
7382 }
7383
7384 /**
7385 * Sets the summary description of the table.
7386 *
7387 * @param a the summary description of the table
7388 * @since 1.3
7389 */
7390 public void setAccessibleSummary(Accessible a) {
7391 Accessible oldSummary = summary;
7392 this.summary = a;
7393 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_SUMMARY_CHANGED,
7394 oldSummary, this.summary);
7395 }
7396
7397 /*
7398 * Returns the total number of rows in this table.
7399 *
7400 * @return the total number of rows in this table
7401 */
7402 public int getAccessibleRowCount() {
7403 return JTable.this.getRowCount();
7404 }
7405
7406 /*
7407 * Returns the total number of columns in the table.
7408 *
7409 * @return the total number of columns in the table
7410 */
7411 public int getAccessibleColumnCount() {
7412 return JTable.this.getColumnCount();
7413 }
7414
7415 /*
7416 * Returns the <code>Accessible</code> at a specified row
7417 * and column in the table.
7418 *
7419 * @param r zero-based row of the table
7420 * @param c zero-based column of the table
7421 * @return the <code>Accessible</code> at the specified row and column
7422 * in the table
7423 */
7424 public Accessible getAccessibleAt(int r, int c) {
7425 return getAccessibleChild((r * getAccessibleColumnCount()) + c);
7426 }
7427
7428 /**
7429 * Returns the number of rows occupied by the <code>Accessible</code>
7430 * at a specified row and column in the table.
7431 *
7432 * @return the number of rows occupied by the <code>Accessible</code>
7433 * at a specified row and column in the table
7434 * @since 1.3
7435 */
7436 public int getAccessibleRowExtentAt(int r, int c) {
7437 return 1;
7438 }
7439
7440 /**
7441 * Returns the number of columns occupied by the
7442 * <code>Accessible</code> at a given (row, column).
7443 *
7444 * @return the number of columns occupied by the <code>Accessible</code>
7445 * at a specified row and column in the table
7446 * @since 1.3
7447 */
7448 public int getAccessibleColumnExtentAt(int r, int c) {
7449 return 1;
7450 }
7451
7452 /**
7453 * Returns the row headers as an <code>AccessibleTable</code>.
7454 *
7455 * @return an <code>AccessibleTable</code> representing the row
7456 * headers
7457 * @since 1.3
7458 */
7459 public AccessibleTable getAccessibleRowHeader() {
7460 // row headers are not supported
7461 return null;
7462 }
7463
7464 /**
7465 * Sets the row headers as an <code>AccessibleTable</code>.
7466 *
7467 * @param a an <code>AccessibleTable</code> representing the row
7468 * headers
7469 * @since 1.3
7470 */
7471 public void setAccessibleRowHeader(AccessibleTable a) {
7472 // row headers are not supported
7473 }
7474
7475 /**
7476 * Returns the column headers as an <code>AccessibleTable</code>.
7477 *
7478 * @return an <code>AccessibleTable</code> representing the column
7479 * headers, or <code>null</code> if the table header is
7480 * <code>null</code>
7481 * @since 1.3
7482 */
7483 public AccessibleTable getAccessibleColumnHeader() {
7484 JTableHeader header = JTable.this.getTableHeader();
7485 return header == null ? null : new AccessibleTableHeader(header);
7486 }
7487
7488 /*
7489 * Private class representing a table column header
7490 */
7491 private class AccessibleTableHeader implements AccessibleTable {
7492 private JTableHeader header;
7493 private TableColumnModel headerModel;
7494
7495 AccessibleTableHeader(JTableHeader header) {
7496 this.header = header;
7497 this.headerModel = header.getColumnModel();
7498 }
7499
7500 /**
7501 * Returns the caption for the table.
7502 *
7503 * @return the caption for the table
7504 */
7505 public Accessible getAccessibleCaption() { return null; }
7506
7507
7508 /**
7509 * Sets the caption for the table.
7510 *
7511 * @param a the caption for the table
7512 */
7513 public void setAccessibleCaption(Accessible a) {}
7514
7515 /**
7516 * Returns the summary description of the table.
7517 *
7518 * @return the summary description of the table
7519 */
7520 public Accessible getAccessibleSummary() { return null; }
7521
7522 /**
7523 * Sets the summary description of the table
7524 *
7525 * @param a the summary description of the table
7526 */
7527 public void setAccessibleSummary(Accessible a) {}
7528
7529 /**
7530 * Returns the number of rows in the table.
7531 *
7532 * @return the number of rows in the table
7533 */
7534 public int getAccessibleRowCount() { return 1; }
7535
7536 /**
7537 * Returns the number of columns in the table.
7538 *
7539 * @return the number of columns in the table
7540 */
7541 public int getAccessibleColumnCount() {
7542 return headerModel.getColumnCount();
7543 }
7544
7545 /**
7546 * Returns the Accessible at a specified row and column
7547 * in the table.
7548 *
7549 * @param row zero-based row of the table
7550 * @param column zero-based column of the table
7551 * @return the Accessible at the specified row and column
7552 */
7553 public Accessible getAccessibleAt(int row, int column) {
7554
7555
7556 // TIGER - 4715503
7557 TableColumn aColumn = headerModel.getColumn(column);
7558 TableCellRenderer renderer = aColumn.getHeaderRenderer();
7559 if (renderer == null) {
7560 renderer = header.getDefaultRenderer();
7561 }
7562 Component component = renderer.getTableCellRendererComponent(
7563 header.getTable(),
7564 aColumn.getHeaderValue(), false, false,
7565 -1, column);
7566
7567 return new AccessibleJTableHeaderCell(row, column,
7568 JTable.this.getTableHeader(),
7569 component);
7570 }
7571
7572 /**
7573 * Returns the number of rows occupied by the Accessible at
7574 * a specified row and column in the table.
7575 *
7576 * @return the number of rows occupied by the Accessible at a
7577 * given specified (row, column)
7578 */
7579 public int getAccessibleRowExtentAt(int r, int c) { return 1; }
7580
7581 /**
7582 * Returns the number of columns occupied by the Accessible at
7583 * a specified row and column in the table.
7584 *
7585 * @return the number of columns occupied by the Accessible at a
7586 * given specified row and column
7587 */
7588 public int getAccessibleColumnExtentAt(int r, int c) { return 1; }
7589
7590 /**
7591 * Returns the row headers as an AccessibleTable.
7592 *
7593 * @return an AccessibleTable representing the row
7594 * headers
7595 */
7596 public AccessibleTable getAccessibleRowHeader() { return null; }
7597
7598 /**
7599 * Sets the row headers.
7600 *
7601 * @param table an AccessibleTable representing the
7602 * row headers
7603 */
7604 public void setAccessibleRowHeader(AccessibleTable table) {}
7605
7606 /**
7607 * Returns the column headers as an AccessibleTable.
7608 *
7609 * @return an AccessibleTable representing the column
7610 * headers
7611 */
7612 public AccessibleTable getAccessibleColumnHeader() { return null; }
7613
7614 /**
7615 * Sets the column headers.
7616 *
7617 * @param table an AccessibleTable representing the
7618 * column headers
7619 * @since 1.3
7620 */
7621 public void setAccessibleColumnHeader(AccessibleTable table) {}
7622
7623 /**
7624 * Returns the description of the specified row in the table.
7625 *
7626 * @param r zero-based row of the table
7627 * @return the description of the row
7628 * @since 1.3
7629 */
7630 public Accessible getAccessibleRowDescription(int r) { return null; }
7631
7632 /**
7633 * Sets the description text of the specified row of the table.
7634 *
7635 * @param r zero-based row of the table
7636 * @param a the description of the row
7637 * @since 1.3
7638 */
7639 public void setAccessibleRowDescription(int r, Accessible a) {}
7640
7641 /**
7642 * Returns the description text of the specified column in the table.
7643 *
7644 * @param c zero-based column of the table
7645 * @return the text description of the column
7646 * @since 1.3
7647 */
7648 public Accessible getAccessibleColumnDescription(int c) { return null; }
7649
7650 /**
7651 * Sets the description text of the specified column in the table.
7652 *
7653 * @param c zero-based column of the table
7654 * @param a the text description of the column
7655 * @since 1.3
7656 */
7657 public void setAccessibleColumnDescription(int c, Accessible a) {}
7658
7659 /**
7660 * Returns a boolean value indicating whether the accessible at
7661 * a specified row and column is selected.
7662 *
7663 * @param r zero-based row of the table
7664 * @param c zero-based column of the table
7665 * @return the boolean value true if the accessible at the
7666 * row and column is selected. Otherwise, the boolean value
7667 * false
7668 * @since 1.3
7669 */
7670 public boolean isAccessibleSelected(int r, int c) { return false; }
7671
7672 /**
7673 * Returns a boolean value indicating whether the specified row
7674 * is selected.
7675 *
7676 * @param r zero-based row of the table
7677 * @return the boolean value true if the specified row is selected.
7678 * Otherwise, false.
7679 * @since 1.3
7680 */
7681 public boolean isAccessibleRowSelected(int r) { return false; }
7682
7683 /**
7684 * Returns a boolean value indicating whether the specified column
7685 * is selected.
7686 *
7687 * @param r zero-based column of the table
7688 * @return the boolean value true if the specified column is selected.
7689 * Otherwise, false.
7690 * @since 1.3
7691 */
7692 public boolean isAccessibleColumnSelected(int c) { return false; }
7693
7694 /**
7695 * Returns the selected rows in a table.
7696 *
7697 * @return an array of selected rows where each element is a
7698 * zero-based row of the table
7699 * @since 1.3
7700 */
7701 public int [] getSelectedAccessibleRows() { return new int[0]; }
7702
7703 /**
7704 * Returns the selected columns in a table.
7705 *
7706 * @return an array of selected columns where each element is a
7707 * zero-based column of the table
7708 * @since 1.3
7709 */
7710 public int [] getSelectedAccessibleColumns() { return new int[0]; }
7711 }
7712
7713
7714 /**
7715 * Sets the column headers as an <code>AccessibleTable</code>.
7716 *
7717 * @param a an <code>AccessibleTable</code> representing the
7718 * column headers
7719 * @since 1.3
7720 */
7721 public void setAccessibleColumnHeader(AccessibleTable a) {
7722 // XXX not implemented
7723 }
7724
7725 /**
7726 * Returns the description of the specified row in the table.
7727 *
7728 * @param r zero-based row of the table
7729 * @return the description of the row
7730 * @since 1.3
7731 */
7732 public Accessible getAccessibleRowDescription(int r) {
7733 if (r < 0 || r >= getAccessibleRowCount()) {
7734 throw new IllegalArgumentException(Integer.toString(r));
7735 }
7736 if (rowDescription == null) {
7737 return null;
7738 } else {
7739 return rowDescription[r];
7740 }
7741 }
7742
7743 /**
7744 * Sets the description text of the specified row of the table.
7745 *
7746 * @param r zero-based row of the table
7747 * @param a the description of the row
7748 * @since 1.3
7749 */
7750 public void setAccessibleRowDescription(int r, Accessible a) {
7751 if (r < 0 || r >= getAccessibleRowCount()) {
7752 throw new IllegalArgumentException(Integer.toString(r));
7753 }
7754 if (rowDescription == null) {
7755 int numRows = getAccessibleRowCount();
7756 rowDescription = new Accessible[numRows];
7757 }
7758 rowDescription[r] = a;
7759 }
7760
7761 /**
7762 * Returns the description of the specified column in the table.
7763 *
7764 * @param c zero-based column of the table
7765 * @return the description of the column
7766 * @since 1.3
7767 */
7768 public Accessible getAccessibleColumnDescription(int c) {
7769 if (c < 0 || c >= getAccessibleColumnCount()) {
7770 throw new IllegalArgumentException(Integer.toString(c));
7771 }
7772 if (columnDescription == null) {
7773 return null;
7774 } else {
7775 return columnDescription[c];
7776 }
7777 }
7778
7779 /**
7780 * Sets the description text of the specified column of the table.
7781 *
7782 * @param c zero-based column of the table
7783 * @param a the description of the column
7784 * @since 1.3
7785 */
7786 public void setAccessibleColumnDescription(int c, Accessible a) {
7787 if (c < 0 || c >= getAccessibleColumnCount()) {
7788 throw new IllegalArgumentException(Integer.toString(c));
7789 }
7790 if (columnDescription == null) {
7791 int numColumns = getAccessibleColumnCount();
7792 columnDescription = new Accessible[numColumns];
7793 }
7794 columnDescription[c] = a;
7795 }
7796
7797 /**
7798 * Returns a boolean value indicating whether the accessible at a
7799 * given (row, column) is selected.
7800 *
7801 * @param r zero-based row of the table
7802 * @param c zero-based column of the table
7803 * @return the boolean value true if the accessible at (row, column)
7804 * is selected; otherwise, the boolean value false
7805 * @since 1.3
7806 */
7807 public boolean isAccessibleSelected(int r, int c) {
7808 return JTable.this.isCellSelected(r, c);
7809 }
7810
7811 /**
7812 * Returns a boolean value indicating whether the specified row
7813 * is selected.
7814 *
7815 * @param r zero-based row of the table
7816 * @return the boolean value true if the specified row is selected;
7817 * otherwise, false
7818 * @since 1.3
7819 */
7820 public boolean isAccessibleRowSelected(int r) {
7821 return JTable.this.isRowSelected(r);
7822 }
7823
7824 /**
7825 * Returns a boolean value indicating whether the specified column
7826 * is selected.
7827 *
7828 * @param c zero-based column of the table
7829 * @return the boolean value true if the specified column is selected;
7830 * otherwise, false
7831 * @since 1.3
7832 */
7833 public boolean isAccessibleColumnSelected(int c) {
7834 return JTable.this.isColumnSelected(c);
7835 }
7836
7837 /**
7838 * Returns the selected rows in a table.
7839 *
7840 * @return an array of selected rows where each element is a
7841 * zero-based row of the table
7842 * @since 1.3
7843 */
7844 public int [] getSelectedAccessibleRows() {
7845 return JTable.this.getSelectedRows();
7846 }
7847
7848 /**
7849 * Returns the selected columns in a table.
7850 *
7851 * @return an array of selected columns where each element is a
7852 * zero-based column of the table
7853 * @since 1.3
7854 */
7855 public int [] getSelectedAccessibleColumns() {
7856 return JTable.this.getSelectedColumns();
7857 }
7858
7859 /**
7860 * Returns the row at a given index into the table.
7861 *
7862 * @param i zero-based index into the table
7863 * @return the row at a given index
7864 * @since 1.3
7865 */
7866 public int getAccessibleRowAtIndex(int i) {
7867 int columnCount = getAccessibleColumnCount();
7868 if (columnCount == 0) {
7869 return -1;
7870 } else {
7871 return (i / columnCount);
7872 }
7873 }
7874
7875 /**
7876 * Returns the column at a given index into the table.
7877 *
7878 * @param i zero-based index into the table
7879 * @return the column at a given index
7880 * @since 1.3
7881 */
7882 public int getAccessibleColumnAtIndex(int i) {
7883 int columnCount = getAccessibleColumnCount();
7884 if (columnCount == 0) {
7885 return -1;
7886 } else {
7887 return (i % columnCount);
7888 }
7889 }
7890
7891 /**
7892 * Returns the index at a given (row, column) in the table.
7893 *
7894 * @param r zero-based row of the table
7895 * @param c zero-based column of the table
7896 * @return the index into the table
7897 * @since 1.3
7898 */
7899 public int getAccessibleIndexAt(int r, int c) {
7900 return ((r * getAccessibleColumnCount()) + c);
7901 }
7902
7903 // end of AccessibleTable implementation --------------------
7904
7905 /**
7906 * The class provides an implementation of the Java Accessibility
7907 * API appropriate to table cells.
7908 */
7909 protected class AccessibleJTableCell extends AccessibleContext
7910 implements Accessible, AccessibleComponent {
7911
7912 private JTable parent;
7913 private int row;
7914 private int column;
7915 private int index;
7916
7917 /**
7918 * Constructs an <code>AccessibleJTableHeaderEntry</code>.
7919 * @since 1.4
7920 */
7921 public AccessibleJTableCell(JTable t, int r, int c, int i) {
7922 parent = t;
7923 row = r;
7924 column = c;
7925 index = i;
7926 this.setAccessibleParent(parent);
7927 }
7928
7929 /**
7930 * Gets the <code>AccessibleContext</code> associated with this
7931 * component. In the implementation of the Java Accessibility
7932 * API for this class, return this object, which is its own
7933 * <code>AccessibleContext</code>.
7934 *
7935 * @return this object
7936 */
7937 public AccessibleContext getAccessibleContext() {
7938 return this;
7939 }
7940
7941 /**
7942 * Gets the AccessibleContext for the table cell renderer.
7943 *
7944 * @return the <code>AccessibleContext</code> for the table
7945 * cell renderer if one exists;
7946 * otherwise, returns <code>null</code>.
7947 * @since 1.6
7948 */
7949 protected AccessibleContext getCurrentAccessibleContext() {
7950 TableColumn aColumn = getColumnModel().getColumn(column);
7951 TableCellRenderer renderer = aColumn.getCellRenderer();
7952 if (renderer == null) {
7953 Class<?> columnClass = getColumnClass(column);
7954 renderer = getDefaultRenderer(columnClass);
7955 }
7956 Component component = renderer.getTableCellRendererComponent(
7957 JTable.this, getValueAt(row, column),
7958 false, false, row, column);
7959 if (component instanceof Accessible) {
7960 return component.getAccessibleContext();
7961 } else {
7962 return null;
7963 }
7964 }
7965
7966 /**
7967 * Gets the table cell renderer component.
7968 *
7969 * @return the table cell renderer component if one exists;
7970 * otherwise, returns <code>null</code>.
7971 * @since 1.6
7972 */
7973 protected Component getCurrentComponent() {
7974 TableColumn aColumn = getColumnModel().getColumn(column);
7975 TableCellRenderer renderer = aColumn.getCellRenderer();
7976 if (renderer == null) {
7977 Class<?> columnClass = getColumnClass(column);
7978 renderer = getDefaultRenderer(columnClass);
7979 }
7980 return renderer.getTableCellRendererComponent(
7981 JTable.this, null, false, false,
7982 row, column);
7983 }
7984
7985 // AccessibleContext methods
7986
7987 /**
7988 * Gets the accessible name of this object.
7989 *
7990 * @return the localized name of the object; <code>null</code>
7991 * if this object does not have a name
7992 */
7993 public String getAccessibleName() {
7994 AccessibleContext ac = getCurrentAccessibleContext();
7995 if (ac != null) {
7996 String name = ac.getAccessibleName();
7997 if ((name != null) && (name != "")) {
7998 // return the cell renderer's AccessibleName
7999 return name;
8000 }
8001 }
8002 if ((accessibleName != null) && (accessibleName != "")) {
8003 return accessibleName;
8004 } else {
8005 // fall back to the client property
8006 return (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
8007 }
8008 }
8009
8010 /**
8011 * Sets the localized accessible name of this object.
8012 *
8013 * @param s the new localized name of the object
8014 */
8015 public void setAccessibleName(String s) {
8016 AccessibleContext ac = getCurrentAccessibleContext();
8017 if (ac != null) {
8018 ac.setAccessibleName(s);
8019 } else {
8020 super.setAccessibleName(s);
8021 }
8022 }
8023
8024 //
8025 // *** should check toolTip text for desc. (needs MouseEvent)
8026 //
8027 /**
8028 * Gets the accessible description of this object.
8029 *
8030 * @return the localized description of the object;
8031 * <code>null</code> if this object does not have
8032 * a description
8033 */
8034 public String getAccessibleDescription() {
8035 AccessibleContext ac = getCurrentAccessibleContext();
8036 if (ac != null) {
8037 return ac.getAccessibleDescription();
8038 } else {
8039 return super.getAccessibleDescription();
8040 }
8041 }
8042
8043 /**
8044 * Sets the accessible description of this object.
8045 *
8046 * @param s the new localized description of the object
8047 */
8048 public void setAccessibleDescription(String s) {
8049 AccessibleContext ac = getCurrentAccessibleContext();
8050 if (ac != null) {
8051 ac.setAccessibleDescription(s);
8052 } else {
8053 super.setAccessibleDescription(s);
8054 }
8055 }
8056
8057 /**
8058 * Gets the role of this object.
8059 *
8060 * @return an instance of <code>AccessibleRole</code>
8061 * describing the role of the object
8062 * @see AccessibleRole
8063 */
8064 public AccessibleRole getAccessibleRole() {
8065 AccessibleContext ac = getCurrentAccessibleContext();
8066 if (ac != null) {
8067 return ac.getAccessibleRole();
8068 } else {
8069 return AccessibleRole.UNKNOWN;
8070 }
8071 }
8072
8073 /**
8074 * Gets the state set of this object.
8075 *
8076 * @return an instance of <code>AccessibleStateSet</code>
8077 * containing the current state set of the object
8078 * @see AccessibleState
8079 */
8080 public AccessibleStateSet getAccessibleStateSet() {
8081 AccessibleContext ac = getCurrentAccessibleContext();
8082 AccessibleStateSet as = null;
8083
8084 if (ac != null) {
8085 as = ac.getAccessibleStateSet();
8086 }
8087 if (as == null) {
8088 as = new AccessibleStateSet();
8089 }
8090 Rectangle rjt = JTable.this.getVisibleRect();
8091 Rectangle rcell = JTable.this.getCellRect(row, column, false);
8092 if (rjt.intersects(rcell)) {
8093 as.add(AccessibleState.SHOWING);
8094 } else {
8095 if (as.contains(AccessibleState.SHOWING)) {
8096 as.remove(AccessibleState.SHOWING);
8097 }
8098 }
8099 if (parent.isCellSelected(row, column)) {
8100 as.add(AccessibleState.SELECTED);
8101 } else if (as.contains(AccessibleState.SELECTED)) {
8102 as.remove(AccessibleState.SELECTED);
8103 }
8104 if ((row == getSelectedRow()) && (column == getSelectedColumn())) {
8105 as.add(AccessibleState.ACTIVE);
8106 }
8107 as.add(AccessibleState.TRANSIENT);
8108 return as;
8109 }
8110
8111 /**
8112 * Gets the <code>Accessible</code> parent of this object.
8113 *
8114 * @return the Accessible parent of this object;
8115 * <code>null</code> if this object does not
8116 * have an <code>Accessible</code> parent
8117 */
8118 public Accessible getAccessibleParent() {
8119 return parent;
8120 }
8121
8122 /**
8123 * Gets the index of this object in its accessible parent.
8124 *
8125 * @return the index of this object in its parent; -1 if this
8126 * object does not have an accessible parent
8127 * @see #getAccessibleParent
8128 */
8129 public int getAccessibleIndexInParent() {
8130 return index;
8131 }
8132
8133 /**
8134 * Returns the number of accessible children in the object.
8135 *
8136 * @return the number of accessible children in the object
8137 */
8138 public int getAccessibleChildrenCount() {
8139 AccessibleContext ac = getCurrentAccessibleContext();
8140 if (ac != null) {
8141 return ac.getAccessibleChildrenCount();
8142 } else {
8143 return 0;
8144 }
8145 }
8146
8147 /**
8148 * Returns the specified <code>Accessible</code> child of the
8149 * object.
8150 *
8151 * @param i zero-based index of child
8152 * @return the <code>Accessible</code> child of the object
8153 */
8154 public Accessible getAccessibleChild(int i) {
8155 AccessibleContext ac = getCurrentAccessibleContext();
8156 if (ac != null) {
8157 Accessible accessibleChild = ac.getAccessibleChild(i);
8158 ac.setAccessibleParent(this);
8159 return accessibleChild;
8160 } else {
8161 return null;
8162 }
8163 }
8164
8165 /**
8166 * Gets the locale of the component. If the component
8167 * does not have a locale, then the locale of its parent
8168 * is returned.
8169 *
8170 * @return this component's locale; if this component does
8171 * not have a locale, the locale of its parent is returned
8172 * @exception IllegalComponentStateException if the
8173 * <code>Component</code> does not have its own locale
8174 * and has not yet been added to a containment hierarchy
8175 * such that the locale can be determined from the
8176 * containing parent
8177 * @see #setLocale
8178 */
8179 public Locale getLocale() {
8180 AccessibleContext ac = getCurrentAccessibleContext();
8181 if (ac != null) {
8182 return ac.getLocale();
8183 } else {
8184 return null;
8185 }
8186 }
8187
8188 /**
8189 * Adds a <code>PropertyChangeListener</code> to the listener list.
8190 * The listener is registered for all properties.
8191 *
8192 * @param l the <code>PropertyChangeListener</code>
8193 * to be added
8194 */
8195 public void addPropertyChangeListener(PropertyChangeListener l) {
8196 AccessibleContext ac = getCurrentAccessibleContext();
8197 if (ac != null) {
8198 ac.addPropertyChangeListener(l);
8199 } else {
8200 super.addPropertyChangeListener(l);
8201 }
8202 }
8203
8204 /**
8205 * Removes a <code>PropertyChangeListener</code> from the
8206 * listener list. This removes a <code>PropertyChangeListener</code>
8207 * that was registered for all properties.
8208 *
8209 * @param l the <code>PropertyChangeListener</code>
8210 * to be removed
8211 */
8212 public void removePropertyChangeListener(PropertyChangeListener l) {
8213 AccessibleContext ac = getCurrentAccessibleContext();
8214 if (ac != null) {
8215 ac.removePropertyChangeListener(l);
8216 } else {
8217 super.removePropertyChangeListener(l);
8218 }
8219 }
8220
8221 /**
8222 * Gets the <code>AccessibleAction</code> associated with this
8223 * object if one exists. Otherwise returns <code>null</code>.
8224 *
8225 * @return the <code>AccessibleAction</code>, or <code>null</code>
8226 */
8227 public AccessibleAction getAccessibleAction() {
8228 return getCurrentAccessibleContext().getAccessibleAction();
8229 }
8230
8231 /**
8232 * Gets the <code>AccessibleComponent</code> associated with
8233 * this object if one exists. Otherwise returns <code>null</code>.
8234 *
8235 * @return the <code>AccessibleComponent</code>, or
8236 * <code>null</code>
8237 */
8238 public AccessibleComponent getAccessibleComponent() {
8239 return this; // to override getBounds()
8240 }
8241
8242 /**
8243 * Gets the <code>AccessibleSelection</code> associated with
8244 * this object if one exists. Otherwise returns <code>null</code>.
8245 *
8246 * @return the <code>AccessibleSelection</code>, or
8247 * <code>null</code>
8248 */
8249 public AccessibleSelection getAccessibleSelection() {
8250 return getCurrentAccessibleContext().getAccessibleSelection();
8251 }
8252
8253 /**
8254 * Gets the <code>AccessibleText</code> associated with this
8255 * object if one exists. Otherwise returns <code>null</code>.
8256 *
8257 * @return the <code>AccessibleText</code>, or <code>null</code>
8258 */
8259 public AccessibleText getAccessibleText() {
8260 return getCurrentAccessibleContext().getAccessibleText();
8261 }
8262
8263 /**
8264 * Gets the <code>AccessibleValue</code> associated with
8265 * this object if one exists. Otherwise returns <code>null</code>.
8266 *
8267 * @return the <code>AccessibleValue</code>, or <code>null</code>
8268 */
8269 public AccessibleValue getAccessibleValue() {
8270 return getCurrentAccessibleContext().getAccessibleValue();
8271 }
8272
8273
8274 // AccessibleComponent methods
8275
8276 /**
8277 * Gets the background color of this object.
8278 *
8279 * @return the background color, if supported, of the object;
8280 * otherwise, <code>null</code>
8281 */
8282 public Color getBackground() {
8283 AccessibleContext ac = getCurrentAccessibleContext();
8284 if (ac instanceof AccessibleComponent) {
8285 return ((AccessibleComponent) ac).getBackground();
8286 } else {
8287 Component c = getCurrentComponent();
8288 if (c != null) {
8289 return c.getBackground();
8290 } else {
8291 return null;
8292 }
8293 }
8294 }
8295
8296 /**
8297 * Sets the background color of this object.
8298 *
8299 * @param c the new <code>Color</code> for the background
8300 */
8301 public void setBackground(Color c) {
8302 AccessibleContext ac = getCurrentAccessibleContext();
8303 if (ac instanceof AccessibleComponent) {
8304 ((AccessibleComponent) ac).setBackground(c);
8305 } else {
8306 Component cp = getCurrentComponent();
8307 if (cp != null) {
8308 cp.setBackground(c);
8309 }
8310 }
8311 }
8312
8313 /**
8314 * Gets the foreground color of this object.
8315 *
8316 * @return the foreground color, if supported, of the object;
8317 * otherwise, <code>null</code>
8318 */
8319 public Color getForeground() {
8320 AccessibleContext ac = getCurrentAccessibleContext();
8321 if (ac instanceof AccessibleComponent) {
8322 return ((AccessibleComponent) ac).getForeground();
8323 } else {
8324 Component c = getCurrentComponent();
8325 if (c != null) {
8326 return c.getForeground();
8327 } else {
8328 return null;
8329 }
8330 }
8331 }
8332
8333 /**
8334 * Sets the foreground color of this object.
8335 *
8336 * @param c the new <code>Color</code> for the foreground
8337 */
8338 public void setForeground(Color c) {
8339 AccessibleContext ac = getCurrentAccessibleContext();
8340 if (ac instanceof AccessibleComponent) {
8341 ((AccessibleComponent) ac).setForeground(c);
8342 } else {
8343 Component cp = getCurrentComponent();
8344 if (cp != null) {
8345 cp.setForeground(c);
8346 }
8347 }
8348 }
8349
8350 /**
8351 * Gets the <code>Cursor</code> of this object.
8352 *
8353 * @return the <code>Cursor</code>, if supported,
8354 * of the object; otherwise, <code>null</code>
8355 */
8356 public Cursor getCursor() {
8357 AccessibleContext ac = getCurrentAccessibleContext();
8358 if (ac instanceof AccessibleComponent) {
8359 return ((AccessibleComponent) ac).getCursor();
8360 } else {
8361 Component c = getCurrentComponent();
8362 if (c != null) {
8363 return c.getCursor();
8364 } else {
8365 Accessible ap = getAccessibleParent();
8366 if (ap instanceof AccessibleComponent) {
8367 return ((AccessibleComponent) ap).getCursor();
8368 } else {
8369 return null;
8370 }
8371 }
8372 }
8373 }
8374
8375 /**
8376 * Sets the <code>Cursor</code> of this object.
8377 *
8378 * @param c the new <code>Cursor</code> for the object
8379 */
8380 public void setCursor(Cursor c) {
8381 AccessibleContext ac = getCurrentAccessibleContext();
8382 if (ac instanceof AccessibleComponent) {
8383 ((AccessibleComponent) ac).setCursor(c);
8384 } else {
8385 Component cp = getCurrentComponent();
8386 if (cp != null) {
8387 cp.setCursor(c);
8388 }
8389 }
8390 }
8391
8392 /**
8393 * Gets the <code>Font</code> of this object.
8394 *
8395 * @return the <code>Font</code>,if supported,
8396 * for the object; otherwise, <code>null</code>
8397 */
8398 public Font getFont() {
8399 AccessibleContext ac = getCurrentAccessibleContext();
8400 if (ac instanceof AccessibleComponent) {
8401 return ((AccessibleComponent) ac).getFont();
8402 } else {
8403 Component c = getCurrentComponent();
8404 if (c != null) {
8405 return c.getFont();
8406 } else {
8407 return null;
8408 }
8409 }
8410 }
8411
8412 /**
8413 * Sets the <code>Font</code> of this object.
8414 *
8415 * @param f the new <code>Font</code> for the object
8416 */
8417 public void setFont(Font f) {
8418 AccessibleContext ac = getCurrentAccessibleContext();
8419 if (ac instanceof AccessibleComponent) {
8420 ((AccessibleComponent) ac).setFont(f);
8421 } else {
8422 Component c = getCurrentComponent();
8423 if (c != null) {
8424 c.setFont(f);
8425 }
8426 }
8427 }
8428
8429 /**
8430 * Gets the <code>FontMetrics</code> of this object.
8431 *
8432 * @param f the <code>Font</code>
8433 * @return the <code>FontMetrics</code> object, if supported;
8434 * otherwise <code>null</code>
8435 * @see #getFont
8436 */
8437 public FontMetrics getFontMetrics(Font f) {
8438 AccessibleContext ac = getCurrentAccessibleContext();
8439 if (ac instanceof AccessibleComponent) {
8440 return ((AccessibleComponent) ac).getFontMetrics(f);
8441 } else {
8442 Component c = getCurrentComponent();
8443 if (c != null) {
8444 return c.getFontMetrics(f);
8445 } else {
8446 return null;
8447 }
8448 }
8449 }
8450
8451 /**
8452 * Determines if the object is enabled.
8453 *
8454 * @return true if object is enabled; otherwise, false
8455 */
8456 public boolean isEnabled() {
8457 AccessibleContext ac = getCurrentAccessibleContext();
8458 if (ac instanceof AccessibleComponent) {
8459 return ((AccessibleComponent) ac).isEnabled();
8460 } else {
8461 Component c = getCurrentComponent();
8462 if (c != null) {
8463 return c.isEnabled();
8464 } else {
8465 return false;
8466 }
8467 }
8468 }
8469
8470 /**
8471 * Sets the enabled state of the object.
8472 *
8473 * @param b if true, enables this object; otherwise, disables it
8474 */
8475 public void setEnabled(boolean b) {
8476 AccessibleContext ac = getCurrentAccessibleContext();
8477 if (ac instanceof AccessibleComponent) {
8478 ((AccessibleComponent) ac).setEnabled(b);
8479 } else {
8480 Component c = getCurrentComponent();
8481 if (c != null) {
8482 c.setEnabled(b);
8483 }
8484 }
8485 }
8486
8487 /**
8488 * Determines if this object is visible. Note: this means that the
8489 * object intends to be visible; however, it may not in fact be
8490 * showing on the screen because one of the objects that this object
8491 * is contained by is not visible. To determine if an object is
8492 * showing on the screen, use <code>isShowing</code>.
8493 *
8494 * @return true if object is visible; otherwise, false
8495 */
8496 public boolean isVisible() {
8497 AccessibleContext ac = getCurrentAccessibleContext();
8498 if (ac instanceof AccessibleComponent) {
8499 return ((AccessibleComponent) ac).isVisible();
8500 } else {
8501 Component c = getCurrentComponent();
8502 if (c != null) {
8503 return c.isVisible();
8504 } else {
8505 return false;
8506 }
8507 }
8508 }
8509
8510 /**
8511 * Sets the visible state of the object.
8512 *
8513 * @param b if true, shows this object; otherwise, hides it
8514 */
8515 public void setVisible(boolean b) {
8516 AccessibleContext ac = getCurrentAccessibleContext();
8517 if (ac instanceof AccessibleComponent) {
8518 ((AccessibleComponent) ac).setVisible(b);
8519 } else {
8520 Component c = getCurrentComponent();
8521 if (c != null) {
8522 c.setVisible(b);
8523 }
8524 }
8525 }
8526
8527 /**
8528 * Determines if the object is showing. This is determined
8529 * by checking the visibility of the object and ancestors
8530 * of the object. Note: this will return true even if the
8531 * object is obscured by another (for example,
8532 * it happens to be underneath a menu that was pulled down).
8533 *
8534 * @return true if the object is showing; otherwise, false
8535 */
8536 public boolean isShowing() {
8537 AccessibleContext ac = getCurrentAccessibleContext();
8538 if (ac instanceof AccessibleComponent) {
8539 if (ac.getAccessibleParent() != null) {
8540 return ((AccessibleComponent) ac).isShowing();
8541 } else {
8542 // Fixes 4529616 - AccessibleJTableCell.isShowing()
8543 // returns false when the cell on the screen
8544 // if no parent
8545 return isVisible();
8546 }
8547 } else {
8548 Component c = getCurrentComponent();
8549 if (c != null) {
8550 return c.isShowing();
8551 } else {
8552 return false;
8553 }
8554 }
8555 }
8556
8557 /**
8558 * Checks whether the specified point is within this
8559 * object's bounds, where the point's x and y coordinates
8560 * are defined to be relative to the coordinate system of
8561 * the object.
8562 *
8563 * @param p the <code>Point</code> relative to the
8564 * coordinate system of the object
8565 * @return true if object contains <code>Point</code>;
8566 * otherwise false
8567 */
8568 public boolean contains(Point p) {
8569 AccessibleContext ac = getCurrentAccessibleContext();
8570 if (ac instanceof AccessibleComponent) {
8571 Rectangle r = ((AccessibleComponent) ac).getBounds();
8572 return r.contains(p);
8573 } else {
8574 Component c = getCurrentComponent();
8575 if (c != null) {
8576 Rectangle r = c.getBounds();
8577 return r.contains(p);
8578 } else {
8579 return getBounds().contains(p);
8580 }
8581 }
8582 }
8583
8584 /**
8585 * Returns the location of the object on the screen.
8586 *
8587 * @return location of object on screen -- can be
8588 * <code>null</code> if this object is not on the screen
8589 */
8590 public Point getLocationOnScreen() {
8591 if (parent != null) {
8592 Point parentLocation = parent.getLocationOnScreen();
8593 Point componentLocation = getLocation();
8594 componentLocation.translate(parentLocation.x, parentLocation.y);
8595 return componentLocation;
8596 } else {
8597 return null;
8598 }
8599 }
8600
8601 /**
8602 * Gets the location of the object relative to the parent
8603 * in the form of a point specifying the object's
8604 * top-left corner in the screen's coordinate space.
8605 *
8606 * @return an instance of <code>Point</code> representing
8607 * the top-left corner of the object's bounds in the
8608 * coordinate space of the screen; <code>null</code> if
8609 * this object or its parent are not on the screen
8610 */
8611 public Point getLocation() {
8612 if (parent != null) {
8613 Rectangle r = parent.getCellRect(row, column, false);
8614 if (r != null) {
8615 return r.getLocation();
8616 }
8617 }
8618 return null;
8619 }
8620
8621 /**
8622 * Sets the location of the object relative to the parent.
8623 */
8624 public void setLocation(Point p) {
8625 // if ((parent != null) && (parent.contains(p))) {
8626 // ensureIndexIsVisible(indexInParent);
8627 // }
8628 }
8629
8630 public Rectangle getBounds() {
8631 if (parent != null) {
8632 return parent.getCellRect(row, column, false);
8633 } else {
8634 return null;
8635 }
8636 }
8637
8638 public void setBounds(Rectangle r) {
8639 AccessibleContext ac = getCurrentAccessibleContext();
8640 if (ac instanceof AccessibleComponent) {
8641 ((AccessibleComponent) ac).setBounds(r);
8642 } else {
8643 Component c = getCurrentComponent();
8644 if (c != null) {
8645 c.setBounds(r);
8646 }
8647 }
8648 }
8649
8650 public Dimension getSize() {
8651 if (parent != null) {
8652 Rectangle r = parent.getCellRect(row, column, false);
8653 if (r != null) {
8654 return r.getSize();
8655 }
8656 }
8657 return null;
8658 }
8659
8660 public void setSize (Dimension d) {
8661 AccessibleContext ac = getCurrentAccessibleContext();
8662 if (ac instanceof AccessibleComponent) {
8663 ((AccessibleComponent) ac).setSize(d);
8664 } else {
8665 Component c = getCurrentComponent();
8666 if (c != null) {
8667 c.setSize(d);
8668 }
8669 }
8670 }
8671
8672 public Accessible getAccessibleAt(Point p) {
8673 AccessibleContext ac = getCurrentAccessibleContext();
8674 if (ac instanceof AccessibleComponent) {
8675 return ((AccessibleComponent) ac).getAccessibleAt(p);
8676 } else {
8677 return null;
8678 }
8679 }
8680
8681 public boolean isFocusTraversable() {
8682 AccessibleContext ac = getCurrentAccessibleContext();
8683 if (ac instanceof AccessibleComponent) {
8684 return ((AccessibleComponent) ac).isFocusTraversable();
8685 } else {
8686 Component c = getCurrentComponent();
8687 if (c != null) {
8688 return c.isFocusTraversable();
8689 } else {
8690 return false;
8691 }
8692 }
8693 }
8694
8695 public void requestFocus() {
8696 AccessibleContext ac = getCurrentAccessibleContext();
8697 if (ac instanceof AccessibleComponent) {
8698 ((AccessibleComponent) ac).requestFocus();
8699 } else {
8700 Component c = getCurrentComponent();
8701 if (c != null) {
8702 c.requestFocus();
8703 }
8704 }
8705 }
8706
8707 public void addFocusListener(FocusListener l) {
8708 AccessibleContext ac = getCurrentAccessibleContext();
8709 if (ac instanceof AccessibleComponent) {
8710 ((AccessibleComponent) ac).addFocusListener(l);
8711 } else {
8712 Component c = getCurrentComponent();
8713 if (c != null) {
8714 c.addFocusListener(l);
8715 }
8716 }
8717 }
8718
8719 public void removeFocusListener(FocusListener l) {
8720 AccessibleContext ac = getCurrentAccessibleContext();
8721 if (ac instanceof AccessibleComponent) {
8722 ((AccessibleComponent) ac).removeFocusListener(l);
8723 } else {
8724 Component c = getCurrentComponent();
8725 if (c != null) {
8726 c.removeFocusListener(l);
8727 }
8728 }
8729 }
8730
8731 } // inner class AccessibleJTableCell
8732
8733 // Begin AccessibleJTableHeader ========== // TIGER - 4715503
8734
8735 /**
8736 * This class implements accessibility for JTable header cells.
8737 */
8738 private class AccessibleJTableHeaderCell extends AccessibleContext
8739 implements Accessible, AccessibleComponent {
8740
8741 private int row;
8742 private int column;
8743 private JTableHeader parent;
8744 private Component rendererComponent;
8745
8746 /**
8747 * Constructs an <code>AccessibleJTableHeaderEntry</code> instance.
8748 *
8749 * @param row header cell row index
8750 * @param column header cell column index
8751 * @param parent header cell parent
8752 * @param rendererComponent component that renders the header cell
8753 */
8754 public AccessibleJTableHeaderCell(int row, int column,
8755 JTableHeader parent,
8756 Component rendererComponent) {
8757 this.row = row;
8758 this.column = column;
8759 this.parent = parent;
8760 this.rendererComponent = rendererComponent;
8761 this.setAccessibleParent(parent);
8762 }
8763
8764 /**
8765 * Gets the <code>AccessibleContext</code> associated with this
8766 * component. In the implementation of the Java Accessibility
8767 * API for this class, return this object, which is its own
8768 * <code>AccessibleContext</code>.
8769 *
8770 * @return this object
8771 */
8772 public AccessibleContext getAccessibleContext() {
8773 return this;
8774 }
8775
8776 /*
8777 * Returns the AccessibleContext for the header cell
8778 * renderer.
8779 */
8780 private AccessibleContext getCurrentAccessibleContext() {
8781 return rendererComponent.getAccessibleContext();
8782 }
8783
8784 /*
8785 * Returns the component that renders the header cell.
8786 */
8787 private Component getCurrentComponent() {
8788 return rendererComponent;
8789 }
8790
8791 // AccessibleContext methods ==========
8792
8793 /**
8794 * Gets the accessible name of this object.
8795 *
8796 * @return the localized name of the object; <code>null</code>
8797 * if this object does not have a name
8798 */
8799 public String getAccessibleName() {
8800 AccessibleContext ac = getCurrentAccessibleContext();
8801 if (ac != null) {
8802 String name = ac.getAccessibleName();
8803 if ((name != null) && (name != "")) {
8804 return ac.getAccessibleName();
8805 }
8806 }
8807 if ((accessibleName != null) && (accessibleName != "")) {
8808 return accessibleName;
8809 } else {
8810 return null;
8811 }
8812 }
8813
8814 /**
8815 * Sets the localized accessible name of this object.
8816 *
8817 * @param s the new localized name of the object
8818 */
8819 public void setAccessibleName(String s) {
8820 AccessibleContext ac = getCurrentAccessibleContext();
8821 if (ac != null) {
8822 ac.setAccessibleName(s);
8823 } else {
8824 super.setAccessibleName(s);
8825 }
8826 }
8827
8828 /**
8829 * Gets the accessible description of this object.
8830 *
8831 * @return the localized description of the object;
8832 * <code>null</code> if this object does not have
8833 * a description
8834 */
8835 public String getAccessibleDescription() {
8836 AccessibleContext ac = getCurrentAccessibleContext();
8837 if (ac != null) {
8838 return ac.getAccessibleDescription();
8839 } else {
8840 return super.getAccessibleDescription();
8841 }
8842 }
8843
8844 /**
8845 * Sets the accessible description of this object.
8846 *
8847 * @param s the new localized description of the object
8848 */
8849 public void setAccessibleDescription(String s) {
8850 AccessibleContext ac = getCurrentAccessibleContext();
8851 if (ac != null) {
8852 ac.setAccessibleDescription(s);
8853 } else {
8854 super.setAccessibleDescription(s);
8855 }
8856 }
8857
8858 /**
8859 * Gets the role of this object.
8860 *
8861 * @return an instance of <code>AccessibleRole</code>
8862 * describing the role of the object
8863 * @see AccessibleRole
8864 */
8865 public AccessibleRole getAccessibleRole() {
8866 AccessibleContext ac = getCurrentAccessibleContext();
8867 if (ac != null) {
8868 return ac.getAccessibleRole();
8869 } else {
8870 return AccessibleRole.UNKNOWN;
8871 }
8872 }
8873
8874 /**
8875 * Gets the state set of this object.
8876 *
8877 * @return an instance of <code>AccessibleStateSet</code>
8878 * containing the current state set of the object
8879 * @see AccessibleState
8880 */
8881 public AccessibleStateSet getAccessibleStateSet() {
8882 AccessibleContext ac = getCurrentAccessibleContext();
8883 AccessibleStateSet as = null;
8884
8885 if (ac != null) {
8886 as = ac.getAccessibleStateSet();
8887 }
8888 if (as == null) {
8889 as = new AccessibleStateSet();
8890 }
8891 Rectangle rjt = JTable.this.getVisibleRect();
8892 Rectangle rcell = JTable.this.getCellRect(row, column, false);
8893 if (rjt.intersects(rcell)) {
8894 as.add(AccessibleState.SHOWING);
8895 } else {
8896 if (as.contains(AccessibleState.SHOWING)) {
8897 as.remove(AccessibleState.SHOWING);
8898 }
8899 }
8900 if (JTable.this.isCellSelected(row, column)) {
8901 as.add(AccessibleState.SELECTED);
8902 } else if (as.contains(AccessibleState.SELECTED)) {
8903 as.remove(AccessibleState.SELECTED);
8904 }
8905 if ((row == getSelectedRow()) && (column == getSelectedColumn())) {
8906 as.add(AccessibleState.ACTIVE);
8907 }
8908 as.add(AccessibleState.TRANSIENT);
8909 return as;
8910 }
8911
8912 /**
8913 * Gets the <code>Accessible</code> parent of this object.
8914 *
8915 * @return the Accessible parent of this object;
8916 * <code>null</code> if this object does not
8917 * have an <code>Accessible</code> parent
8918 */
8919 public Accessible getAccessibleParent() {
8920 return parent;
8921 }
8922
8923 /**
8924 * Gets the index of this object in its accessible parent.
8925 *
8926 * @return the index of this object in its parent; -1 if this
8927 * object does not have an accessible parent
8928 * @see #getAccessibleParent
8929 */
8930 public int getAccessibleIndexInParent() {
8931 return column;
8932 }
8933
8934 /**
8935 * Returns the number of accessible children in the object.
8936 *
8937 * @return the number of accessible children in the object
8938 */
8939 public int getAccessibleChildrenCount() {
8940 AccessibleContext ac = getCurrentAccessibleContext();
8941 if (ac != null) {
8942 return ac.getAccessibleChildrenCount();
8943 } else {
8944 return 0;
8945 }
8946 }
8947
8948 /**
8949 * Returns the specified <code>Accessible</code> child of the
8950 * object.
8951 *
8952 * @param i zero-based index of child
8953 * @return the <code>Accessible</code> child of the object
8954 */
8955 public Accessible getAccessibleChild(int i) {
8956 AccessibleContext ac = getCurrentAccessibleContext();
8957 if (ac != null) {
8958 Accessible accessibleChild = ac.getAccessibleChild(i);
8959 ac.setAccessibleParent(this);
8960 return accessibleChild;
8961 } else {
8962 return null;
8963 }
8964 }
8965
8966 /**
8967 * Gets the locale of the component. If the component
8968 * does not have a locale, then the locale of its parent
8969 * is returned.
8970 *
8971 * @return this component's locale; if this component does
8972 * not have a locale, the locale of its parent is returned
8973 * @exception IllegalComponentStateException if the
8974 * <code>Component</code> does not have its own locale
8975 * and has not yet been added to a containment hierarchy
8976 * such that the locale can be determined from the
8977 * containing parent
8978 * @see #setLocale
8979 */
8980 public Locale getLocale() {
8981 AccessibleContext ac = getCurrentAccessibleContext();
8982 if (ac != null) {
8983 return ac.getLocale();
8984 } else {
8985 return null;
8986 }
8987 }
8988
8989 /**
8990 * Adds a <code>PropertyChangeListener</code> to the listener list.
8991 * The listener is registered for all properties.
8992 *
8993 * @param l the <code>PropertyChangeListener</code>
8994 * to be added
8995 */
8996 public void addPropertyChangeListener(PropertyChangeListener l) {
8997 AccessibleContext ac = getCurrentAccessibleContext();
8998 if (ac != null) {
8999 ac.addPropertyChangeListener(l);
9000 } else {
9001 super.addPropertyChangeListener(l);
9002 }
9003 }
9004
9005 /**
9006 * Removes a <code>PropertyChangeListener</code> from the
9007 * listener list. This removes a <code>PropertyChangeListener</code>
9008 * that was registered for all properties.
9009 *
9010 * @param l the <code>PropertyChangeListener</code>
9011 * to be removed
9012 */
9013 public void removePropertyChangeListener(PropertyChangeListener l) {
9014 AccessibleContext ac = getCurrentAccessibleContext();
9015 if (ac != null) {
9016 ac.removePropertyChangeListener(l);
9017 } else {
9018 super.removePropertyChangeListener(l);
9019 }
9020 }
9021
9022 /**
9023 * Gets the <code>AccessibleAction</code> associated with this
9024 * object if one exists. Otherwise returns <code>null</code>.
9025 *
9026 * @return the <code>AccessibleAction</code>, or <code>null</code>
9027 */
9028 public AccessibleAction getAccessibleAction() {
9029 return getCurrentAccessibleContext().getAccessibleAction();
9030 }
9031
9032 /**
9033 * Gets the <code>AccessibleComponent</code> associated with
9034 * this object if one exists. Otherwise returns <code>null</code>.
9035 *
9036 * @return the <code>AccessibleComponent</code>, or
9037 * <code>null</code>
9038 */
9039 public AccessibleComponent getAccessibleComponent() {
9040 return this; // to override getBounds()
9041 }
9042
9043 /**
9044 * Gets the <code>AccessibleSelection</code> associated with
9045 * this object if one exists. Otherwise returns <code>null</code>.
9046 *
9047 * @return the <code>AccessibleSelection</code>, or
9048 * <code>null</code>
9049 */
9050 public AccessibleSelection getAccessibleSelection() {
9051 return getCurrentAccessibleContext().getAccessibleSelection();
9052 }
9053
9054 /**
9055 * Gets the <code>AccessibleText</code> associated with this
9056 * object if one exists. Otherwise returns <code>null</code>.
9057 *
9058 * @return the <code>AccessibleText</code>, or <code>null</code>
9059 */
9060 public AccessibleText getAccessibleText() {
9061 return getCurrentAccessibleContext().getAccessibleText();
9062 }
9063
9064 /**
9065 * Gets the <code>AccessibleValue</code> associated with
9066 * this object if one exists. Otherwise returns <code>null</code>.
9067 *
9068 * @return the <code>AccessibleValue</code>, or <code>null</code>
9069 */
9070 public AccessibleValue getAccessibleValue() {
9071 return getCurrentAccessibleContext().getAccessibleValue();
9072 }
9073
9074
9075 // AccessibleComponent methods ==========
9076
9077 /**
9078 * Gets the background color of this object.
9079 *
9080 * @return the background color, if supported, of the object;
9081 * otherwise, <code>null</code>
9082 */
9083 public Color getBackground() {
9084 AccessibleContext ac = getCurrentAccessibleContext();
9085 if (ac instanceof AccessibleComponent) {
9086 return ((AccessibleComponent) ac).getBackground();
9087 } else {
9088 Component c = getCurrentComponent();
9089 if (c != null) {
9090 return c.getBackground();
9091 } else {
9092 return null;
9093 }
9094 }
9095 }
9096
9097 /**
9098 * Sets the background color of this object.
9099 *
9100 * @param c the new <code>Color</code> for the background
9101 */
9102 public void setBackground(Color c) {
9103 AccessibleContext ac = getCurrentAccessibleContext();
9104 if (ac instanceof AccessibleComponent) {
9105 ((AccessibleComponent) ac).setBackground(c);
9106 } else {
9107 Component cp = getCurrentComponent();
9108 if (cp != null) {
9109 cp.setBackground(c);
9110 }
9111 }
9112 }
9113
9114 /**
9115 * Gets the foreground color of this object.
9116 *
9117 * @return the foreground color, if supported, of the object;
9118 * otherwise, <code>null</code>
9119 */
9120 public Color getForeground() {
9121 AccessibleContext ac = getCurrentAccessibleContext();
9122 if (ac instanceof AccessibleComponent) {
9123 return ((AccessibleComponent) ac).getForeground();
9124 } else {
9125 Component c = getCurrentComponent();
9126 if (c != null) {
9127 return c.getForeground();
9128 } else {
9129 return null;
9130 }
9131 }
9132 }
9133
9134 /**
9135 * Sets the foreground color of this object.
9136 *
9137 * @param c the new <code>Color</code> for the foreground
9138 */
9139 public void setForeground(Color c) {
9140 AccessibleContext ac = getCurrentAccessibleContext();
9141 if (ac instanceof AccessibleComponent) {
9142 ((AccessibleComponent) ac).setForeground(c);
9143 } else {
9144 Component cp = getCurrentComponent();
9145 if (cp != null) {
9146 cp.setForeground(c);
9147 }
9148 }
9149 }
9150
9151 /**
9152 * Gets the <code>Cursor</code> of this object.
9153 *
9154 * @return the <code>Cursor</code>, if supported,
9155 * of the object; otherwise, <code>null</code>
9156 */
9157 public Cursor getCursor() {
9158 AccessibleContext ac = getCurrentAccessibleContext();
9159 if (ac instanceof AccessibleComponent) {
9160 return ((AccessibleComponent) ac).getCursor();
9161 } else {
9162 Component c = getCurrentComponent();
9163 if (c != null) {
9164 return c.getCursor();
9165 } else {
9166 Accessible ap = getAccessibleParent();
9167 if (ap instanceof AccessibleComponent) {
9168 return ((AccessibleComponent) ap).getCursor();
9169 } else {
9170 return null;
9171 }
9172 }
9173 }
9174 }
9175
9176 /**
9177 * Sets the <code>Cursor</code> of this object.
9178 *
9179 * @param c the new <code>Cursor</code> for the object
9180 */
9181 public void setCursor(Cursor c) {
9182 AccessibleContext ac = getCurrentAccessibleContext();
9183 if (ac instanceof AccessibleComponent) {
9184 ((AccessibleComponent) ac).setCursor(c);
9185 } else {
9186 Component cp = getCurrentComponent();
9187 if (cp != null) {
9188 cp.setCursor(c);
9189 }
9190 }
9191 }
9192
9193 /**
9194 * Gets the <code>Font</code> of this object.
9195 *
9196 * @return the <code>Font</code>,if supported,
9197 * for the object; otherwise, <code>null</code>
9198 */
9199 public Font getFont() {
9200 AccessibleContext ac = getCurrentAccessibleContext();
9201 if (ac instanceof AccessibleComponent) {
9202 return ((AccessibleComponent) ac).getFont();
9203 } else {
9204 Component c = getCurrentComponent();
9205 if (c != null) {
9206 return c.getFont();
9207 } else {
9208 return null;
9209 }
9210 }
9211 }
9212
9213 /**
9214 * Sets the <code>Font</code> of this object.
9215 *
9216 * @param f the new <code>Font</code> for the object
9217 */
9218 public void setFont(Font f) {
9219 AccessibleContext ac = getCurrentAccessibleContext();
9220 if (ac instanceof AccessibleComponent) {
9221 ((AccessibleComponent) ac).setFont(f);
9222 } else {
9223 Component c = getCurrentComponent();
9224 if (c != null) {
9225 c.setFont(f);
9226 }
9227 }
9228 }
9229
9230 /**
9231 * Gets the <code>FontMetrics</code> of this object.
9232 *
9233 * @param f the <code>Font</code>
9234 * @return the <code>FontMetrics</code> object, if supported;
9235 * otherwise <code>null</code>
9236 * @see #getFont
9237 */
9238 public FontMetrics getFontMetrics(Font f) {
9239 AccessibleContext ac = getCurrentAccessibleContext();
9240 if (ac instanceof AccessibleComponent) {
9241 return ((AccessibleComponent) ac).getFontMetrics(f);
9242 } else {
9243 Component c = getCurrentComponent();
9244 if (c != null) {
9245 return c.getFontMetrics(f);
9246 } else {
9247 return null;
9248 }
9249 }
9250 }
9251
9252 /**
9253 * Determines if the object is enabled.
9254 *
9255 * @return true if object is enabled; otherwise, false
9256 */
9257 public boolean isEnabled() {
9258 AccessibleContext ac = getCurrentAccessibleContext();
9259 if (ac instanceof AccessibleComponent) {
9260 return ((AccessibleComponent) ac).isEnabled();
9261 } else {
9262 Component c = getCurrentComponent();
9263 if (c != null) {
9264 return c.isEnabled();
9265 } else {
9266 return false;
9267 }
9268 }
9269 }
9270
9271 /**
9272 * Sets the enabled state of the object.
9273 *
9274 * @param b if true, enables this object; otherwise, disables it
9275 */
9276 public void setEnabled(boolean b) {
9277 AccessibleContext ac = getCurrentAccessibleContext();
9278 if (ac instanceof AccessibleComponent) {
9279 ((AccessibleComponent) ac).setEnabled(b);
9280 } else {
9281 Component c = getCurrentComponent();
9282 if (c != null) {
9283 c.setEnabled(b);
9284 }
9285 }
9286 }
9287
9288 /**
9289 * Determines if this object is visible. Note: this means that the
9290 * object intends to be visible; however, it may not in fact be
9291 * showing on the screen because one of the objects that this object
9292 * is contained by is not visible. To determine if an object is
9293 * showing on the screen, use <code>isShowing</code>.
9294 *
9295 * @return true if object is visible; otherwise, false
9296 */
9297 public boolean isVisible() {
9298 AccessibleContext ac = getCurrentAccessibleContext();
9299 if (ac instanceof AccessibleComponent) {
9300 return ((AccessibleComponent) ac).isVisible();
9301 } else {
9302 Component c = getCurrentComponent();
9303 if (c != null) {
9304 return c.isVisible();
9305 } else {
9306 return false;
9307 }
9308 }
9309 }
9310
9311 /**
9312 * Sets the visible state of the object.
9313 *
9314 * @param b if true, shows this object; otherwise, hides it
9315 */
9316 public void setVisible(boolean b) {
9317 AccessibleContext ac = getCurrentAccessibleContext();
9318 if (ac instanceof AccessibleComponent) {
9319 ((AccessibleComponent) ac).setVisible(b);
9320 } else {
9321 Component c = getCurrentComponent();
9322 if (c != null) {
9323 c.setVisible(b);
9324 }
9325 }
9326 }
9327
9328 /**
9329 * Determines if the object is showing. This is determined
9330 * by checking the visibility of the object and ancestors
9331 * of the object. Note: this will return true even if the
9332 * object is obscured by another (for example,
9333 * it happens to be underneath a menu that was pulled down).
9334 *
9335 * @return true if the object is showing; otherwise, false
9336 */
9337 public boolean isShowing() {
9338 AccessibleContext ac = getCurrentAccessibleContext();
9339 if (ac instanceof AccessibleComponent) {
9340 if (ac.getAccessibleParent() != null) {
9341 return ((AccessibleComponent) ac).isShowing();
9342 } else {
9343 // Fixes 4529616 - AccessibleJTableCell.isShowing()
9344 // returns false when the cell on the screen
9345 // if no parent
9346 return isVisible();
9347 }
9348 } else {
9349 Component c = getCurrentComponent();
9350 if (c != null) {
9351 return c.isShowing();
9352 } else {
9353 return false;
9354 }
9355 }
9356 }
9357
9358 /**
9359 * Checks whether the specified point is within this
9360 * object's bounds, where the point's x and y coordinates
9361 * are defined to be relative to the coordinate system of
9362 * the object.
9363 *
9364 * @param p the <code>Point</code> relative to the
9365 * coordinate system of the object
9366 * @return true if object contains <code>Point</code>;
9367 * otherwise false
9368 */
9369 public boolean contains(Point p) {
9370 AccessibleContext ac = getCurrentAccessibleContext();
9371 if (ac instanceof AccessibleComponent) {
9372 Rectangle r = ((AccessibleComponent) ac).getBounds();
9373 return r.contains(p);
9374 } else {
9375 Component c = getCurrentComponent();
9376 if (c != null) {
9377 Rectangle r = c.getBounds();
9378 return r.contains(p);
9379 } else {
9380 return getBounds().contains(p);
9381 }
9382 }
9383 }
9384
9385 /**
9386 * Returns the location of the object on the screen.
9387 *
9388 * @return location of object on screen -- can be
9389 * <code>null</code> if this object is not on the screen
9390 */
9391 public Point getLocationOnScreen() {
9392 if (parent != null) {
9393 Point parentLocation = parent.getLocationOnScreen();
9394 Point componentLocation = getLocation();
9395 componentLocation.translate(parentLocation.x, parentLocation.y);
9396 return componentLocation;
9397 } else {
9398 return null;
9399 }
9400 }
9401
9402 /**
9403 * Gets the location of the object relative to the parent
9404 * in the form of a point specifying the object's
9405 * top-left corner in the screen's coordinate space.
9406 *
9407 * @return an instance of <code>Point</code> representing
9408 * the top-left corner of the object's bounds in the
9409 * coordinate space of the screen; <code>null</code> if
9410 * this object or its parent are not on the screen
9411 */
9412 public Point getLocation() {
9413 if (parent != null) {
9414 Rectangle r = parent.getHeaderRect(column);
9415 if (r != null) {
9416 return r.getLocation();
9417 }
9418 }
9419 return null;
9420 }
9421
9422 /**
9423 * Sets the location of the object relative to the parent.
9424 * @param p the new position for the top-left corner
9425 * @see #getLocation
9426 */
9427 public void setLocation(Point p) {
9428 }
9429
9430 /**
9431 * Gets the bounds of this object in the form of a Rectangle object.
9432 * The bounds specify this object's width, height, and location
9433 * relative to its parent.
9434 *
9435 * @return A rectangle indicating this component's bounds; null if
9436 * this object is not on the screen.
9437 * @see #contains
9438 */
9439 public Rectangle getBounds() {
9440 if (parent != null) {
9441 return parent.getHeaderRect(column);
9442 } else {
9443 return null;
9444 }
9445 }
9446
9447 /**
9448 * Sets the bounds of this object in the form of a Rectangle object.
9449 * The bounds specify this object's width, height, and location
9450 * relative to its parent.
9451 *
9452 * @param r rectangle indicating this component's bounds
9453 * @see #getBounds
9454 */
9455 public void setBounds(Rectangle r) {
9456 AccessibleContext ac = getCurrentAccessibleContext();
9457 if (ac instanceof AccessibleComponent) {
9458 ((AccessibleComponent) ac).setBounds(r);
9459 } else {
9460 Component c = getCurrentComponent();
9461 if (c != null) {
9462 c.setBounds(r);
9463 }
9464 }
9465 }
9466
9467 /**
9468 * Returns the size of this object in the form of a Dimension object.
9469 * The height field of the Dimension object contains this object's
9470 * height, and the width field of the Dimension object contains this
9471 * object's width.
9472 *
9473 * @return A Dimension object that indicates the size of this component;
9474 * null if this object is not on the screen
9475 * @see #setSize
9476 */
9477 public Dimension getSize() {
9478 if (parent != null) {
9479 Rectangle r = parent.getHeaderRect(column);
9480 if (r != null) {
9481 return r.getSize();
9482 }
9483 }
9484 return null;
9485 }
9486
9487 /**
9488 * Resizes this object so that it has width and height.
9489 *
9490 * @param d The dimension specifying the new size of the object.
9491 * @see #getSize
9492 */
9493 public void setSize (Dimension d) {
9494 AccessibleContext ac = getCurrentAccessibleContext();
9495 if (ac instanceof AccessibleComponent) {
9496 ((AccessibleComponent) ac).setSize(d);
9497 } else {
9498 Component c = getCurrentComponent();
9499 if (c != null) {
9500 c.setSize(d);
9501 }
9502 }
9503 }
9504
9505 /**
9506 * Returns the Accessible child, if one exists, contained at the local
9507 * coordinate Point.
9508 *
9509 * @param p The point relative to the coordinate system of this object.
9510 * @return the Accessible, if it exists, at the specified location;
9511 * otherwise null
9512 */
9513 public Accessible getAccessibleAt(Point p) {
9514 AccessibleContext ac = getCurrentAccessibleContext();
9515 if (ac instanceof AccessibleComponent) {
9516 return ((AccessibleComponent) ac).getAccessibleAt(p);
9517 } else {
9518 return null;
9519 }
9520 }
9521
9522 /**
9523 * Returns whether this object can accept focus or not. Objects that
9524 * can accept focus will also have the AccessibleState.FOCUSABLE state
9525 * set in their AccessibleStateSets.
9526 *
9527 * @return true if object can accept focus; otherwise false
9528 * @see AccessibleContext#getAccessibleStateSet
9529 * @see AccessibleState#FOCUSABLE
9530 * @see AccessibleState#FOCUSED
9531 * @see AccessibleStateSet
9532 */
9533 public boolean isFocusTraversable() {
9534 AccessibleContext ac = getCurrentAccessibleContext();
9535 if (ac instanceof AccessibleComponent) {
9536 return ((AccessibleComponent) ac).isFocusTraversable();
9537 } else {
9538 Component c = getCurrentComponent();
9539 if (c != null) {
9540 return c.isFocusTraversable();
9541 } else {
9542 return false;
9543 }
9544 }
9545 }
9546
9547 /**
9548 * Requests focus for this object. If this object cannot accept focus,
9549 * nothing will happen. Otherwise, the object will attempt to take
9550 * focus.
9551 * @see #isFocusTraversable
9552 */
9553 public void requestFocus() {
9554 AccessibleContext ac = getCurrentAccessibleContext();
9555 if (ac instanceof AccessibleComponent) {
9556 ((AccessibleComponent) ac).requestFocus();
9557 } else {
9558 Component c = getCurrentComponent();
9559 if (c != null) {
9560 c.requestFocus();
9561 }
9562 }
9563 }
9564
9565 /**
9566 * Adds the specified focus listener to receive focus events from this
9567 * component.
9568 *
9569 * @param l the focus listener
9570 * @see #removeFocusListener
9571 */
9572 public void addFocusListener(FocusListener l) {
9573 AccessibleContext ac = getCurrentAccessibleContext();
9574 if (ac instanceof AccessibleComponent) {
9575 ((AccessibleComponent) ac).addFocusListener(l);
9576 } else {
9577 Component c = getCurrentComponent();
9578 if (c != null) {
9579 c.addFocusListener(l);
9580 }
9581 }
9582 }
9583
9584 /**
9585 * Removes the specified focus listener so it no longer receives focus
9586 * events from this component.
9587 *
9588 * @param l the focus listener
9589 * @see #addFocusListener
9590 */
9591 public void removeFocusListener(FocusListener l) {
9592 AccessibleContext ac = getCurrentAccessibleContext();
9593 if (ac instanceof AccessibleComponent) {
9594 ((AccessibleComponent) ac).removeFocusListener(l);
9595 } else {
9596 Component c = getCurrentComponent();
9597 if (c != null) {
9598 c.removeFocusListener(l);
9599 }
9600 }
9601 }
9602
9603 } // inner class AccessibleJTableHeaderCell
9604
9605 } // inner class AccessibleJTable
9606
9607 } // End of Class JTable